| 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 | }}} |