I have a two models that are similar, but not exactly the same. Here's the best abstraction of the problem that I can come up with.
class Cat(models.Model):
name = models.TextField()
breed = models.TextField()
class Dog(models.Model):
name = models.TextField()
color = models.TextField()
And now I need to make another model like this.
class Pet(models.Model):
favoriteFood = models.TextField()
isCat = models.BooleanField()
animal = models.ForeignKey(?????????)
My problem is that the animal field of the Pet model is going to be a foreign key to either the Cat or the Dog model depending on the value of isCat. How can I do that?
Now, I know this is an unusual/awkward schema in the first place, but I wasn't involved in its creation and I can't change it. I just have to support it. I'm writing these models for an existing database.
You should see Generic relations.
Generic relations is a direct answer.
Another option for this use case is: django-polymorphic :)
Related
I have these models
class Tree(models.Model):
field = models.TextField()
class TaskProgress(models.Model):
base_task = models.ForeignKey(BaseTask, on_delete=models.CASCADE)
tree = models.ForeignKey(Tree, on_delete=models.CASCADE)
class BaseTask(models.Model):
trees=models.ManyToManyField(Tree, through='TaskProgress')
class TaskType1(BaseTask):
child1_field = models.TextField()
class TaskType2(BaseTask):
child2_field = models.TextField()
how to get all taskprogress when related to TaskType2 ,
TaskProgress.objects.filter(???)
I added extra field on BaseTask class
TASK_TYPE =[('I','Irrigation'),('C','Care'),('A','Assessment'),('O','Other')]
class BaseTask(models.Model):
trees=models.ManyToManyField(Tree, through='TaskProgress')
worker = models.ManyToManyField(User)
task_type = models.CharField(max_length=1,choices=TASK_TYPE,null=True)
And the filter will be like this
TaskProgress.objects.filter(base_task__task = "I")
I do not think what you are asking is possible, if the models are designed like described. The base_task ForeignKey is specifically pointing at a BaseTask. Even though TaskType1 and TaskType2 inherit from BaseTask, they have no relation in the database. They only look similar.
Option 1: Look into Generic Relations in Django. Basically it allows you to have a ForeignKey relation with more than one type of model. I would not recommend it though. Generic relations are a mess if you don't know want you are doing.
Option 2: Rethink your layout. Maybe you can move the relation to the two TaskTypes instead and adress them via related_name.
class TaskProgress(models.Model):
# base_task = models.ForeignKey(BaseTask, on_delete=models.CASCADE)
tree = models.ForeignKey(Tree, on_delete=models.CASCADE)
class TaskType1(BaseTask):
task_progress = models.OneToOneField(TaskProgress, related_name='task_type_1'
child1_field = models.TextField()
class TaskType2(BaseTask):
task_progress = models.OneToOneField(TaskProgress, related_name='task_type_2'
child2_field = models.TextField()
This way you create a one-to-one-relation between the TaskProgress and the TaskType. You should be able to query one or the other by checking whether a relation exists, e.g. all TaskProgress instances with a relation to a TaskType1 instance.
# Query all TaskProgress instances, that have a TaskType1
TaskProgress.objects.filter(task_type_1__isnull=False)
Lets say that i have 2 models :
Class OrderEvent(models.Model):
isPaid = models.Booleanfield()
Class Participant(models.Model):
orderEvent = models.ForeignKey(OrderEvent)
participantFirstName = models.CharField()
participantLastName = models.CharField()
#etc...
And i want to get all the participants where Orderevent.isPaid = True.
I think that i struggle to do something very simple...
It is very simple;
Participant.objects.filter(orderEvent__isPaid=True)
As a suggestion you can follow,
naming conventions from here.
Django making queries from here.
I am not getting why people write foreign key in two way and what is the purpose of this? are they both same or any different?
I notice some people write like:
author = models.ForeignKey(Author, on_delete=models.CASCADE)
and some people write it like:
author = models.ForeignKey('Author', on_delete=models.CASCADE)
What is different between these? is there any special purpose of writing like this or they both are same?
What is different between these? is there any special purpose of writing like this or they both are same?
They both result in the same link yes. The string will later be "resolved", and eventually the ForeignKey will point to the Author model.
Using strings however is sometimes the only way to make references however, if the models to which you target need to be defined yet. For example in the case of a cyclic reference.
Imagine for example that you define relations like:
class Author(models.Model):
name = models.CharField(max_length=128)
favorite_book = models.ForeignKey(Book, null=True, on_delete=models.SET_NULL)
class Book(models.Model):
title = models.CharField(max_length=128)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
Here a Book refers to an Author, and an Author refers to a Book. But since the Book class is not constructed at the time you construct the ForeignKey, this will give a NameError.
We can not define the Author after we defined the Book, since then we refer to the Author before it is constructed (and this thus will again yield a NameError).
We can however use strings here, th avoid the circular reference, like:
class Author(models.Model):
name = models.CharField(max_length=128)
favorite_book = models.ForeignKey('Book', null=True, on_delete=models.SET_NULL)
class Book(models.Model):
title = models.CharField(max_length=128)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
By using a string, it is fine for the Python interpreter, since you do not use an identifier that is not yet defined, and Django will then, when the models are loaded, replace the strings with a reference to the corresponding model.
The documentation on a ForeignKey [Django-doc]:
If you need to create a relationship on a model that has not yet been defined, you can use the name of the model, rather than the model object itself (...)
If the model is defined in another app, then you can refer to it with app_name.ModelName.
Say you have laid your models out like this:
models/
__init__.py
model_a.py
model_b.py
This is a common layout when you have an app with a lot of models and you want to better organize your code. Now say ModelA has a foreign key to ModelB and ModelB has a foreign key to ModelA. You cannot have both files importing the other model because you would have a circular import.
Referencing another model by string allows you to "lazily" reference another model that it has not yet loaded, this solves the problem of having circular imports
First of all I have to admit that I'm quite new to all this coding stuff but as I couldn't find a proper solution doing it myself and learning to code is probably the best way.
Anyway, I'm trying to build an app to show different titleholders, championships and stuff like that. After reading the Django documentation I figured out I have to use intermediate models as a better way. My old models.py looks like this:
class Person(models.Model):
first_name = models.CharField(max_length=64)
last_name = models.CharField(max_length=64)
[...]
class Team(models.Model):
name = models.CharField(max_length=64)
team_member_one = models.ForeignKey(Person)
team_member_two = models.ForeignKey(Person)
class Championship(models.Model):
name = models.CharField(max_length=128)
status = models.BooleanField(default=True)
class Titleholder(models.Model):
championship = models.ForeignKey(Championship)
date_won = models.DateField(null=True,blank=True)
date_lost = models.DateField(null=True,blank=True)
titleholder_one = models.ForeignKey(Person,related_name='titleholder_one',null=True,blank=True)
titleholder_two = models.ForeignKey(Person,related_name='titleholder_two',null=True,blank=True)
Championships can be won by either individuals or teams, depending if it's a singles or team championship, that's why I had to foreign keys in the Titleholder class. Looking at the Django documentation this just seems false. On the other hand, for me as a beginner, the intermediate model in the example just doesn't seem to fit my model in any way.
Long story short: can anyone point me in the right direction on how to build the model the right way? Note: this is merely a question on how to build the models and displaying it in the Django admin, I don't even talk about building the templates as of now.
Help would be greatly appreciated! Thanks in advance guys.
So I will take it up from scratch. You seem to be somewhat new to E-R Database Modelling. If I was trying to do what you do, I would create my models the following way.
Firstly, Team would've been my "corner" model (I use this term to mean models that do not have any FK fields), and then Person model would come into play as follows.
class Team(models.Model):
name = models.CharField(max_length=64)
class Person(models.Model):
first_name = models.CharField(max_length=64)
last_name = models.CharField(max_length=64)
team = models.ForeignKey(to=Team, null=True, blank=True, related_name='members')
This effectively makes the models scalable, and even if you are never going to have more than two people in a team, this is good practice.
Next comes the Championship model. I would connect this model directly with the Person model as a many-to-many relationship with a 'through' model as follows.
class Championship(models.Model):
name = models.CharField(max_length=64)
status = models.BooleanField(default=False) # This is not a great name for a field. I think should be more meaningful.
winners = models.ManyToManyField(to=Person, related_name='championships', through='Title')
class Title(models.Model):
championship = models.ForeignKey(to=Championship, related_name='titles')
winner = models.ForeignKey(to=Person, related_name='titles')
date = models.DateField(null=True, blank=True)
This is just the way I would've done it, based on what I understood. I am sure I did not understand everything that you're trying to do. As my understanding changes, I might modify these models to suit my need.
Another approach that can be taken is by using a GenericForeignKey field to create a field that could be a FK to either the Team model or the Person model. Or another thing that can be changed could be you adding another model to hold details of each time a championship has been held. There are many ways to go about it, and no one correct way.
Let me know if you have any questions, or anything I haven't dealt with. I will try and modify the answer as per the need.
I have the following two models
class Author(Model):
name = CharField()
class Publication(Model):
title = CharField()
And I use an intermediary table to keep track of the list of authors. The ordering of authors matter; and that's why I don't use Django's ManyToManyField.
class PubAuthor(Model):
author = models.ForeignKey(Author)
pubentry = models.ForeignKey(Publication)
position = models.IntegerField(max_length=3)
The problem is, given a publication, what's the most efficient way to get all authors for the publication?
I can use pubentry.pubauthor_set.select_related().order_by('position'), but then it this will generate one query each time I access the author's name.
I've found out the answer.
In publications:
def authors(self):
return Author.objects.all().filter(
pubauthor__pubentry__id=self.id).order_by('pubauthor__position')