I am working on the Django
Writing your first Django app, part 2
In the section "2.4.4 Playing with the API".
If someone could explain to me how does the underscore works in this code q.choice_set.all()
It's pretty simple. Let's consider these two models:
class Author(models.Model):
name = models.Charfield(max_lenght=100)
class Book(models.Model):
author = models.ForeignKey(Author, on_delete=models.CASCADE)
name = models.Charfield(max_lenght=100)
Explain:
We have some Authors that each author has some books.
but what if we want to get all books of a specific author?
let's assume we want all books that written by Sara:
my_author = Author.objects.get(name='Sara')
Here is the magic! Django will automatically generate the backward relation for this object called book_set which contains all books writen by Sara.
The naming rule is straightforward, <related_model_name>_set.
So, you can get those books by writing this line:
sara_books = my_author.book_set.all()
It's nice, isn't it ? wait, it could be nicer!
book_set is not a good name for me, i want a more human readable name!
you can easily change this by adding related_name to the author field of Book model:
class Book(models.Model):
author = models.ForeignKey(Author, on_delete=models.CASCADE, related_name='her_books') # you can name it anything you want
name = models.Charfield(max_lenght=100)
then, you can get same books like this:
sara_books = my_author.her_books.all()
it's now more human readable :) Hope you get it.
Related
I have a model Article:
class Article(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey(User, on_delete=models.SET(get_sentinel_user))
class Meta:
unique_together = ('author', 'title')
The method get_sentinel_user:
def get_sentinel_user():
return get_user_model().objects.get_or_create(username='deleted')[0]
Thus, using on_delete=models.SET(get_sentinel_user), I save posts after deleting the author (if he agrees to this, I ask him in a separate form). And instead of a real author, I set a "stub"-user as the author for his posts.
In such a situation, there is a problem with the uniqueness condition. If two users had articles with the same title and then one of them was deleted, then an error will occur when trying to delete the second user. Accordingly, I would like this error to be absent. So that the uniqueness condition applies only to real users and their articles. I tried this:
class Meta:
constraints = [
models.UniqueConstraint(
name='unique_combiantion_article_name_and_author',
fields=['title', 'author'],
condition=~models.Q(author__username='deleted')
)
]
But then I made sure that this does not work, we cannot use author__username.
Right now I'm thinking about just overriding validate_unique method. But first I would like to ask for advice. Maybe there is some other solution for this problem.
Thanks for any help.
I want to define a class for books.
A book consists of several chapters.
A chapter consists of several sections.
A section consists of several subsections.
Is it okay to do it as follows?
class Book(models.Model):
pass
class Chapter(models.Model):
book = models.ForeignKey('Book', on_delete=models.CASCADE)
class Section(models.Model):
chapter = models.ForeignKey('Chapter', on_delete=models.CASCADE)
class SubSection(models.Model):
section = models.ForeignKey('Section', on_delete=models.CASCADE)
content = models.TextField()
Or is there a better way?
Yes, this is the standard way to do it, as it maps the corresponding SQL closely.
In Chapter you define book as a foreign key
Book class will automatically get a variable called chapter_set, which contains a list of chapter
This alone proves that you are doing it correctly. See related managers for more information
Same for subchapter
I am working to figure out the model for a Django project: an app to track Books.
Among other fields, every Book has either/both a Printer and a Publisher, which are basically identical. So, here's how it stands:
class Book(models.Model):
title = models.CharField(max_length=100)
printer = models.ForeignKey('Printer')
publisher = models.ForeignKey('Publisher')
class Printer(models.Model):
name = models.CharField(max_length=100)
location = models.CharField(max_length=100)
class Publisher(models.Model):
name = models.CharField(max_length=100)
location = models.CharField(max_length=100)
It seems to me this is bad database form: it's not DRY. In addition, quite often, a Book might be printed by a firm which publishes the same or another book: in other words, the tables can overlap. So, the two models Printer and Publisher should really be combined, while they need to remain distinct in the admin.
My question: how best to do this? Should I create another model, Firm, and create one-to-one relationships between it and Printer/Publisher?
The Django way to handle that is to create an Abstract Base Model. This is the DRY way to create your models. Here is the code:
class BaseModel(models.Model):
name = models.CharField(max_length=100)
location = models.CharField(max_length=100)
class Meta:
abstract = True
class Printer(BaseModel):
pass
class Publisher(BaseModel):
pass
This will allow you to specify redundant fields only once. Also, if you need to add any extra fields to one model, just add them instead of using pass.
I'm trying to work out how to calculate the number of books written by an author. The book model sets up the manytomany field with the author model. I have no idea how even google something like this which I thought would be very simple to do but I don't even know how to start.
Here's the stripped down code:
class Author(models.Model):
first_name = models.CharField()
last_name = models.CharField()
def books(self):
"""Return a list of comma separated list of books by the author"""
pass
class Book(models.Model):
title = models.CharField()
authors = models.ManyToManyField(Author)
Maybe I could do a filter query to get books by a particular author but it doesn't seem like the django way to do it? I'm also not sure how to pass the filter function a python object rather than text.
Thanks
when you define a foreignkey or manytomany field, django sets up a reverse relation for you, which in your case is book_set. so something like:
def books(self):
books = self.book_set.all()
return ', '.join([book.title for book in books])
see related objects in the docs
I'm just learning Django so feel free to correct me in any of my assumptions. I probably just need my mindset adjusted.
What I'm trying to do is creating a "class" in an OOP style. For example, let's say we're designing a bunch of Rooms. Each Room has Furniture. And each piece of Furniture has a Type and a Color. What I can see so far is that I can have
class FurnitureType(models.Model):
name = models.CharField(max_length=200)
class FurnitureColor(models.Model):
name = models.CharField(max_length=50)
class FurniturePiece(models.Model):
type = models.ForeignKey(FurnitureType)
color = models.ForeignKey(FurnitureColor)
sqft = models.IntegerField()
name = models.CharField(max_length=200)
class Room(models.Model):
name = models.CharField(max_length=200)
furnitures = models.ManyToManyField(FurniturePiece)
The problem is that each FurniturePiece has to have a unique name if I'm picking it out of the Django admin interface. If one person creates "Green Couch" then no one else can have a "Green Couch". What I'm wondering is if a) I need to learn more about Django UI and this is the right way to design this in Django or b) I have a bad design for this domain
The reason I want Furniture name to be unique is because 10 people could create a "Green Couch" each with a different sqft.
I don't get the problem with unique name. You can just specify it to be unique:
class FurniturePiece(models.Model):
type = models.ForeignKey(FurnitureType)
color = models.ForeignKey(FurnitureColor)
sqft = models.IntegerField()
name = models.CharField(max_length=200, unique=True)
I don't know whether you have to learn about Django UI or not. I guess you have to learn how to define models. The admin interface is just a generated interface based on your models. You can change the interface in certain aspects without changing the models, but besides that, there is less to learn about the admin interface.
I suggest you follow a tutorial like the djangobook, to get a good start with Django.
I think, the problem that you have is not how to use Django but more that you don't know how to model your application in general.
First you have to think about which entities do yo have (like Room, Furniture, etc.).
Then think about what relations they have.
Afterwards you can model them in Django. Of course in order to do this you have to know how to model the relations. The syntax might be Django specific but the logical relations are not. E.g. a many-to-many relation is not something Django specific, this is a term used in databases to express a certain relationship.
Djangos models are just abstraction of the database design below.
E.g you specified a many-to-many relationship between Room and FurniturePiece.
Now the question: Is this what you want? It means that a piece of furniture can belong to more than one room. This sounds strange. So maybe you want to model it that a piece of furniture only belongs to one room. But a room should still have several pieces of furniture. We therefore define a relationship from FurniturePiece to Room.
In Django, we can express this with:
class FurniturePiece(models.Model):
room = models.ForeignKey(Room)
type = models.ForeignKey(FurnitureType)
color = models.ForeignKey(FurnitureColor)
sqft = models.IntegerField()
name = models.CharField(max_length=200)
Maybe you should first learn about relational databases to get the basics before you model your application with Django.
It might be that this not necessary in order to create an application in Django. But it will definitely help you to understand whats going on, for every ORM not just Django's.
Why does each FurniturePiece need to have a unique name? It seems to me that if you remove that constraint everything just works.
(as an aside you seem to have accidentally dropped the models.Model base class for all but the Room model).
This is how I would do it:
class Room(models.Model):
name = models.CharField(max_length=255)
pieces = models.ManyToManyField('FurniturePiece')
class FurniturePiece(models.Model):
itemid = models.CharField(max_length=20, unique=True) # This is what I would require to be unique.
name = models.CharField(max_length=255)
type = models.ForeignKey('FurnitureType') # Note I put 'FurnitureType' in quotes because it hasn't been written yet (coming next).
color = models.ForeignKey('FurnitureColor') # Same here.
width_in_inches = models.PositiveIntegerField()
length_in_inches = models.PositiveIntegerField()
# Next is the property decorator which allows a method to be called without using ()
#property
def sqft(self):
return (self.length_in_inches * self.width_in_inches) / 144 # Obviously this is rough.
class FurnitureType(models.Model):
name = models.CharField(max_length=255)
class FurnitureColor(models.Model):
name = models.CharField(max_length=255)
Envision objects as real life objects, and you'll have a deeper understanding of the code as well. The reason for my sqft method is that data is best when normalized as much as possible. If you have a width and length, then when somebody asks, you have length, width, sqft, and if you add height, volume as well.