| | 1 | = Removing the magic = |
| | 2 | |
| | 3 | This document proposes a new, cleaner and less magical Django database API. |
| | 4 | |
| | 5 | == Model definition == |
| | 6 | |
| | 7 | Difference: Import is from {{{django.db.models}}} instead of {{{django.core.meta}}}. This is easier to remember. {{{models}}} may not be the best name for it. |
| | 8 | |
| | 9 | {{{ |
| | 10 | #!python |
| | 11 | from django.db import models |
| | 12 | |
| | 13 | class Person(models.Model): |
| | 14 | first_name = models.CharField(maxlength=30) |
| | 15 | |
| | 16 | last_name = models.CharField(maxlength=30) |
| | 17 | }}} |
| | 18 | |
| | 19 | Properties are allowed. |
| | 20 | |
| | 21 | {{{ |
| | 22 | #!python |
| | 23 | from django.db import models |
| | 24 | |
| | 25 | class Person(models.Model): |
| | 26 | first_name = models.CharField(maxlength=30) |
| | 27 | last_name = models.CharField(maxlength=30) |
| | 28 | |
| | 29 | def _get_full_name(self): |
| | 30 | return "%s %s" % (self.first_name, self.last_name) |
| | 31 | full_name = property(_get_full_name) |
| | 32 | }}} |
| | 33 | |
| | 34 | == Database connection == |
| | 35 | |
| | 36 | Old: |
| | 37 | {{{ |
| | 38 | #!python |
| | 39 | from django.core.db import db |
| | 40 | cursor = db.cursor() |
| | 41 | }}} |
| | 42 | |
| | 43 | New: |
| | 44 | {{{ |
| | 45 | #!python |
| | 46 | from django.db import connection |
| | 47 | cursor = connection.cursor() |
| | 48 | }}} |
| | 49 | |
| | 50 | This is easier to remember, clearer and more consistent. |
| | 51 | |
| | 52 | == API usage: Object creation == |
| | 53 | |
| | 54 | Import the model class directly from the module in which it was defined. No more {{{django.models.*}}} magic. |
| | 55 | |
| | 56 | {{{ |
| | 57 | #!python |
| | 58 | from myproject.people.models import Person |
| | 59 | p = Person(first_name='John', last_name='Smith') |
| | 60 | p.save() |
| | 61 | }}} |
| | 62 | |
| | 63 | == API usage: Table-level functions == |
| | 64 | |
| | 65 | All "table-level" functions -- ways of retrieving records tablewide rather than performing instance-specific tasks -- are accessed via a model instance's {{{objects}}} attribute. They aren't direct methods of a model object because we want to keep the "table-wide" and "row-specific" namespaces separate. |
| | 66 | |
| | 67 | {{{ |
| | 68 | #!python |
| | 69 | from myproject.people.models import Person |
| | 70 | p_list = Person.objects.get_list() |
| | 71 | p = Person.objects.get_object() |
| | 72 | }}} |
| | 73 | |
| | 74 | This doesn't work from an instance. |
| | 75 | |
| | 76 | {{{ |
| | 77 | #!python |
| | 78 | p = Person.objects.get_object(pk=1) |
| | 79 | p.objects.get_list() # Will raise an exception. |
| | 80 | }}} |
| | 81 | |
| | 82 | If a model already has an {{{objects}}} attribute, the field-accessor attribute is renamed to {{{objects_}}}. |
| | 83 | |
| | 84 | {{{ |
| | 85 | #!python |
| | 86 | class Person(models.Model): |
| | 87 | first_name = models.CharField(maxlength=30) |
| | 88 | last_name = models.CharField(maxlength=30) |
| | 89 | objects = models.TextField() |
| | 90 | |
| | 91 | p = Person(first_name='Mary', last_name='Jones', objects_='Hello there.') |
| | 92 | p.save() |
| | 93 | p.objects_ == 'Hello there.' |
| | 94 | }}} |
| | 95 | |
| | 96 | == API usage: Overridding model methods (and pre- and post-save hooks) == |
| | 97 | |
| | 98 | Proper subclassing of methods will now work, so you can subclass the automatic {{{save()}}} and {{{delete()}}} methods. This removes the need for the {{{_pre_save()}}} and {{{_post_save()}}} hooks. Example: |
| | 99 | |
| | 100 | {{{ |
| | 101 | #!python |
| | 102 | class Person(models.Model): |
| | 103 | first_name = models.CharField(maxlength=30) |
| | 104 | last_name = models.CharField(maxlength=30) |
| | 105 | |
| | 106 | def save(self): |
| | 107 | self.do_something() |
| | 108 | super(Person, self).save(self) # Call the "real" save() method. |
| | 109 | self.do_something_else() |
| | 110 | }}} |
| | 111 | |
| | 112 | You can even skip saving (as requested in #1014). |
| | 113 | |
| | 114 | {{{ |
| | 115 | #!python |
| | 116 | class Person(models.Model): |
| | 117 | first_name = models.CharField(maxlength=30) |
| | 118 | last_name = models.CharField(maxlength=30) |
| | 119 | |
| | 120 | def save(self): |
| | 121 | if datetime.date.today() > datetime.date(2005, 1, 1): |
| | 122 | super(Person, self).save(self) # Call the "real" save() method. |
| | 123 | else: |
| | 124 | # Don't save. |
| | 125 | pass |
| | 126 | }}} |
| | 127 | |
| | 128 | == API usage: Overridding table-level functions == |
| | 129 | |
| | 130 | You can also override any table-level functions, such as {{{get_list()}}} or {{{get_object()}}}. Do this by creating a custom {{{models.Manager}}} subclass and passing it to your model. |
| | 131 | |
| | 132 | {{{ |
| | 133 | #!python |
| | 134 | from django.db import models |
| | 135 | class PersonManager(models.Manager): |
| | 136 | def get_list(self, **kwargs): |
| | 137 | # Changes get_list() to hard-code a limit=10. |
| | 138 | kwargs['limit'] = 10 |
| | 139 | return super(PersonManager, self).get_list(self, **kwargs) # Call the "real" get_list() method. |
| | 140 | |
| | 141 | class Person(models.Model): |
| | 142 | first_name = models.CharField(maxlength=30) |
| | 143 | last_name = models.CharField(maxlength=30) |
| | 144 | class META: |
| | 145 | manager = PersonManager() |
| | 146 | }}} |
| | 147 | |
| | 148 | == API usage: Specifying lookups == |
| | 149 | |
| | 150 | Old: |
| | 151 | {{{ |
| | 152 | #!python |
| | 153 | people.get_list(first_name__exact='Adrian') |
| | 154 | }}} |
| | 155 | |
| | 156 | New: |
| | 157 | {{{ |
| | 158 | #!python |
| | 159 | Person.objects.get_list(Person.q.first_name == 'Adrian') |
| | 160 | }}} |
| | 161 | |
| | 162 | The old syntax will still be supported and documented, indefinitely. The new syntax will be implemented as a thin wrapper around the old syntax. See patch on #851 for implementation. |
| | 163 | |
| | 164 | == Other "module"-level members: Automatic manipulators and ObjectDoesNotExist exception == |
| | 165 | |
| | 166 | {{{ |
| | 167 | #!python |
| | 168 | Person.get_add_manipulator() |
| | 169 | Person.get_change_manipulator() |
| | 170 | Person.DoesNotExist |
| | 171 | }}} |