简体   繁体   中英

Writing a proper data model in Django

I'm trying to write a Django model, but as I read further and further in the docs, I find out that I have to take care about too many things and I'm getting really confused with all the considerations I have to bear in mind.

My most essential doubt is "When I define two classes, in which do I include the reference to the other when..."

  • a) class A has only one class B instances
  • b) class A must have one and just one class B instance
  • c) class A has many class B instances
  • d) class A is a type of class B (so as Restaurant is a type of Business)

"... and how do I create/manage these two classes when creating those objects".

I hope someone may explain it easier. I think that with these four things clear I may be able to write the model myself.

Django document is still a good place for you to start, and it is really comprehensive for both beginners and professionals. No other shortcut to master it except reading its document and playing with the example.

case a) class A has only one class B instances

UserModel stands for A , UserProfile stands for B .

Suppose we have a UserModel , and need one more table to keep the extra information, let's say UserProfile . And we allow UserProfile to be null. then we can use OneToOneField to bind these two class.

model definition

from django.db import models

class UserModel(models.Model):
    name = models.CharField('username', max_length=20)

class UserProfile(models.Model):
    nickname = models.CharField('nick name', max_length=50, blank=True, null=True)
    # blank tells the validator, this field could be blank, and null tells the db engine, this field could be null.
    user = models.OneToOneField(UserModel, blank=True, null=True,  on_delete=models.CASCADE)

How to access A from B and vice versa

u = UserModel.objects.get(pk=1)
nickname = u.userprofile.nickname    # to access B from A, you can use the model name lowercase name for reference.

profile = UserModel.objects.get(pk=1)
username = profile.user.username     # To access A from B, you can use the model name lowercase name for reference.

case b) class A must have one and just one class B instance

UserModel stands for A , UserProfile stands for B .

For the model definition, it is almost the same as case a). But we need to make sure B is coexistence with A, so we will need to remove Blank and null . And we must use post_save hook to make suer B is also created with A is saved.

Model definition

from django.db import models
from django.dispatch import receiver
from django.db.models.signals import post_save, pre_save

class UserModel(models.Model):
    name = models.CharField('username', max_length=20)

class UserProfile(models.Model):
    nickname = models.CharField('nick name', max_length=50)
    user = models.OneToOneField(UserModel,on_delete=models.CASCADE)

@receiver(post_save, sender=UserModel)
def create_auth_token(sender, instance=None, created=False, **kwargs):
    # This hook is automatically called when the UserModel is created, this is used to make suer UserProfile is also created when UserModel is created.
    if created:
        UserProfile.objects.create(user=instance)

How to access A from B and vice versa

The same as case a).

case c) class A has many class B instances

Author stands for A , Book stands for B .

For example, Author is a model to save the book author information, and one author may write zero or many books, let's named it to Book as model. (Suppose only allow one author for each book).

class Author(models.Model):
    name = models.CharField('name of the author', max_length=20)

class Book(models.Model):
    book_name = models.CharField('name of the book', max_length=50)
    author = models.ForeignKey(Author, related_name='books')

How to access A from B and vice versa

# access B from A
author = Author.objects.get(pk=1)
books = author.books
# first book
book_name = books[0].book_name

# access A from B
book = Book.objects.get(pk=1)
author_name = book.author.name

If you allow one book has many authors, then we should use ManyToManyField instead of ForeignKey . The Book model definition changed to this:

class BookAuthor(models.Model):
    author = model.ForeignKey(Author)
    book = model.ForeignKey(Book)
    publish_at = model.DateTimeField(auto_add=True)

class Book(models.Model):
    book_name = models.CharField('name of the book', max_length=50)
    # through is used to tell django, we have defined another relationship table to bind Author and Book model.
    authors = models.ManyToManyField(Author, through='BookAuthor')

How to access A from B and vice versa

author = Author.objects.get(pk=1)
first_book_name = author.book_set.order_by('publish_at')[0].book_name

book = Book.objects.get(pk=1)
author_names = [author.name for author in book.author_set.all()]

case d) class A is a type of class B (so as Restaurant is a type of Business)

Place stands for B , Library and Restaurant stands for A .

For example, we defined a generic class Place , and Library could be a place, same as Restaurant .

Model definition

class Place(models.Model):
   address = models.CharField('address', max_length=100)

class Library(Place):
   num_of_books = models.IntegerField('NO. of books in the library')

class Restaurant(Place):
   restaurant_type = models.CharField('The type of the restaurant', max_length=10)

How to use A and B

lib = Library.objects.get(pk=1)
lib.address  # get the library address
lib.num_of_books  # get the no. of books in this library 

rest = Restaurant.objects.get(pk=1)
rest.address  # get the restaurant address
rest.restaurant_type  # get the restaurant type

For the above case, django will create a Place table in db, because it is also a model, if you don't what it to be created, and treat it as Abstract class as other programming language, you can redefined it as follow:

class Place(models.Model):
   address = models.CharField('address', max_length=100)

   class Meta:
       abstract = True

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM