Model Introduction

Model RelationShips

ManyToOne

  1. Keyword: “models.ForeignKey”, for example:
from django.db import models

class Reporter(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

    def __str__(self):              # __unicode__ on Python 2
        return "%s %s" % (self.first_name, self.last_name)

class Article(models.Model):
    headline = models.CharField(max_length=100)
    reporter = models.ForeignKey(Reporter)

    def __str__(self):              # __unicode__ on Python 2
        return self.headline
  1. Reverse access with reporter.article_set (modellowercase_set), direct access just use filed name, for example:
>>>r = Reporter(first_name='John', last_name='Smith')
>>>r.save()
>>>a = Article(headline="This is a test", reporter=r)
>>>a.save()
>>>r.article_set.all()  # use article_set
[<Article: This is a test>]
>>> a.reporter    # just use field name
<Reporter: John Smith>
>>>r.article_set.create( headline="headline1") #add article
>>>a2 = Article.objects.create( headline="headline2")
>>>r.article_set.add(a2) #add article
>>>r.article_set = [a2] # reinit article
  1. Reverse query with article (lowcasemodel), direct query just use fieldname:
>>> Article.objects.filter(reporter__first_name__icontains='john') # use reporter
[<Article: This is a test>]
>>> Reporter.objects.filter(article__headline__contains="test")    # use article
[<Reporter: John Smith>]

ManyToMany

  1. Keyword: “models.ManyToManyField”, for example:
class Publication(models.Model):
    title = models.CharField(max_length=30)

    def __str__(self):              # __unicode__ on Python 2
        return self.title

class Article(models.Model):
    headline = models.CharField(max_length=100)
    publications = models.ManyToManyField(Publication)

    def __str__(self):              # __unicode__ on Python 2
        return self.headline
  1. Reverse access with “article_set” (modellowercase_set) like “ForeignKey”, direct access just use filed name, for example:
>>>p1 = Publication(title='The Python Journal')
>>>p1.save()
>>>a1 = Article(headline='Django lets you build Web apps easily')
>>>a1.save()
>>>a1.publications.add(p1)   # just use filed name: publications
>>>p1.article_set.all()      # use article_set
[<Artic: Django lets you build Web apps easily>]
  1. Reverse query with article (lowcasemodel), direct query just use fieldname:
>>> Publication.objects.filter(article__headline__icontains="django") # use article
[<Publication: The Python Journal>]
>>> Article.objects.filter(publications__title__icontains="python")   # use publications
[<Article: Django lets you build Web apps easily>]
  1. If you need to associate data with the relationship between two models, use intermediate model with “through”:
class Person(models.Model):
    name = models.CharField(max_length=128)

    def __str__(self):              # __unicode__ on Python 2
        return self.name

class Group(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField(Person, through='Membership')

    def __str__(self):              # __unicode__ on Python 2
        return self.name

class Membership(models.Model):
    person = models.ForeignKey(Person)
    group = models.ForeignKey(Group)
    date_joined = models.DateField()
    invite_reason = models.CharField(max_length=64)
  1. If there are more than one Foreign key to the source model in intermediate model, you must use “through_fields”:
class Person(models.Model):
    name = models.CharField(max_length=128)

    def __str__(self):              # __unicode__ on Python 2
        return self.name

class Group(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField(Person, through='Membership', through_fields=('group', 'person'))

    def __str__(self):              # __unicode__ on Python 2
        return self.name

class Membership(models.Model):
    person = models.ForeignKey(Person)
    inviter = models.ForeignKey(Person, related_name="membership_invites")
    group = models.ForeignKey(Group)
    date_joined = models.DateField()
    invite_reason = models.CharField(max_length=64)
Note:
if you use “intermediate model”, you can’t use group.memebers.add(), group.memebers.create(), group.members=[], because you need to specify all the detail for the relationship required by the Membership model.

OneToOne

  1. Keyworkd: “models.OneToOneField”, for example:
from django.db import models

class Place(models.Model):
    name = models.CharField(max_length=50)
    address = models.CharField(max_length=80)

    def __str__(self):              # __unicode__ on Python 2
        return "%s the place" % self.name

class Restaurant(models.Model):
    place = models.OneToOneField(Place, primary_key=True)
    serves_hot_dogs = models.BooleanField(default=False)
    serves_pizza = models.BooleanField(default=False)
  1. Reverse access with place.restaurant (modellowercase), direct access just use field name like restaurant.place, for example:
>>>p1 = Place(name='Demon Dogs', address='944 W. Fullerton')
>>>p1.save()
>>>r = Restaurant(place=p1, serves_hot_dogs=True, serves_pizza=False)
>>>r.save()
>>> r.place   # use place
<Place: Demon Dogs the place>
>>> p1.restaurant # use resaurant
<Restaurant: Demon Dogs the restaurant>
  1. Reverse query with resaurant (lowcasemodel), direct query just use field name, for example:
>>>Place.objects.filter(restaurant__serves_hot_dogs=True)    # use resaurant
>>>Restaurant.objects.filter(place__name__icontains="hello") # use place

Common Usage for ForeignKey, OneToOneField, ManyToManyFiled:

  1. If you need to create a relationship on a model that has not yet been defined, use the name of the model, rather than the model object itself:
from django.db import models

class Restaurant(models.Model):
    place = models.OneToOneField('Place', primary_key=True) # with quote

class Place(models.Model):
    name = models.CharField(max_length=50)
    address = models.CharField(max_length=80)

    def __str__(self):              # __unicode__ on Python 2
        return "%s the place" % self.name
  1. To create a recursive relationship: an object that has relationship with itself, use “self”, for example:
class Person(models.Model):
    friends = models.ManyToManyField("self")
  1. Use “related_name” and “related_query_name”, for example, if we change ForeignKey to:
from django.db import models

class Reporter(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

    def __str__(self):              # __unicode__ on Python 2
        return "%s %s" % (self.first_name, self.last_name)

class Article(models.Model):
    headline = models.CharField(max_length=100)
    #reporter = models.ForeignKey(Reporter)
    reporter = models.ForeignKey(Reporter, related_name="articles", related_query_name="reporter_article")

    def __str__(self):              # __unicode__ on Python 2
        return self.headline

then reverse access will change from “repoter.article_set” to “reporter.articles”, because the value of “related_name” is used, for example:

>>>r1= Reporter.objects.get(first_name="John")
>>>r1.articles.all()   #not article_set
[<Article: This is a test>]

when query, reverse query will change from “article” to “reporter_article”, because the value of “related_query_name” is used, for example:

>>>Reporter.objects.filter(reporter_article__headline__contains="test")
[<Reporter: John Smith>]
Note:
If you only define “related_name”, not “related_query_name”, then “related_query_name” is the value of “related_name” as default.

Model Meta

Meta Options

Adding class Meta to a model is completely optional. If needed, do it like so:

class Person(models.Model):
    name = models.CharField(max_length=64)
    family_name = models.CharField(max_length=64)
 class Meta:
     verbose_name_plural = "people"
     index_together = [["name", "family_name"],]
     unique_together = ("name", "family_name")
     ordering = ['-name', 'family_name']

complete meta options list:

  1. abstract: If abstract = True, this model will be an abstract base class
  2. app_label: If a model exists outside of the standard locations (models.py or a models package in an app), the model must define which app it is part of:
app_label = 'myapp'
  1. db_table: the name of the database table to use for the model, default is app_model
  2. db_tablespace: PostgreSQL and Oracle support tablespaces. SQLite and MySQL don’t.
  3. get_latest_by: the name of an orderable field in the model, typically a DateField, DateTimeField, or IntegerField. This specifies the default field to use in your model Manager’s latest() and earliest() methods: Entry.objects.latest(‘pub_date’)
  4. managed: Defaults to True, meaning Django will create the appropriate database tables in migrate or as part of migrations and remove them as part of a flush management command. That is, Django manages the database tables’ lifecycles.
  5. order_with_respect_to: this is almost always used with related objects to allow them to be ordered with respect to a parent object: 7.1 When order_with_respect_to is set, two additional methods are provided to retrieve and to set the order of the related objects:get_RELATED_order() and set_RELATED_order(), where RELATED is the lowercased model name. 7.2 The related objects also get two methods, get_next_in_order() and get_previous_in_order() 7.3 for example:
from django.db import models

class Question(models.Model):
    text = models.TextField()
# ...

class Answer(models.Model):
    question = models.ForeignKey(Question)

    class Meta:
        order_with_respect_to = 'question'

>>> question = Question.objects.get(id=1)
>>> question.get_answer_order()
   [1, 2, 3]
>>> question.set_answer_order([3, 1, 2])
>>> answer = Answer.objects.get(id=2)
>>> answer.get_next_in_order()
 <Answer: 3>
>>> answer.get_previous_in_order()
  <Answer: 1>
  1. ordering: the default ordering for the object, for use when obtaining lists of objects: “-” means “descending order”, ”?” means random, no leading “-” means “ascending”, see above
  2. permissions:
  3. default_permissions: add, change, delete
  4. proxy: if proxy = True, a model which subclasses another model will be treated as a proxy model, no extra table will be created for the submodel. The submodel can’t add new fields, you can just change things like the default model ordering or the default manager
  5. select_on_save: usually there is no need to set this attribute. The default is False.
  6. unique_together: sets of field names that, taken together, must be unique, a ManyToManyField cannot be included in unique_together. see above.
  7. index_together: Sets of field names that, taken together, are indexed, see above
  8. verbose_name: A human-readable name for the object, singular
  9. verbose_name_plural: the plural name for the object, defaulst is verbose_name + ‘s’