I have an entity in my application which has some attribute like auth.User but it has also some extra attributes. So I created a OneToOne relationship with auth.User
class UserEntity(models.Model):
user = models.OneToOneField(User)
... other fields ...
I also have a Person model which is UserEntity so created it as this:
class Person (models.Model):
userEntity = models.OneToOneField(UserEntity)
... other fields ...
There are many different attributes like addresses,experiences,education and other details that I want to associate with my Person. A Person can have many addresses,experiences,education,speciality
I have a Speciality Model like this.
class Speciality(models.Model):
name = models.CharField()
code = models.CharField()
... other fields ...
The problem is how should I my data model be designed so that I can retrieve a person/user with all addresses,expereiences,specialities etc. I can associate each Person/user with Speciality since Speciality is an independent model and can exist without a user. Currently I have created another model for each e.g for Speciality I have created
class PersonSpeciality(models.Model):
person = models.ForeignKey(Person)
specialities = models.ForeignKey(Speciality)
Can I design it in a better way so I can have fast searching and retrievals and no mess.
Thank you
If a Person can have multiple Specialities, then you can implement a oneToMany relationship, just by adding a foreignKey:
class Speciality(models.Model):
name = models.CharField()
code = models.CharField()
person = models.ForeignKey(Person)
However, if you have several Persons with the same specialities (let's say 10 Persons are Painters), you would have 10 different Painters entries.
To avoid duplication, you can implement a ManyToMany relationship :
class Speciality(models.Model):
name = models.CharField()
code = models.CharField()
person = models.ManyToManyField(Person)
Django will create a third table in your DB to handle that (you don't need to write your PersonSpeciality class).
Related
I have a situation where I need to be able to add multiple copies of the same object to a many-to-many relationship.
Let's say that the problem is recording the types of furniture someone has. Here are my base models:
class Person(models.Model):
name = models.CharField(max_length=100)
class Furniture(models.Model):
furniture_name = models.CharField(max_length=100) #e.g. Chair, Sofa.
Lets say I want to record that Sam owns 3 chairs and 2 sofas. But I don't want to make more than one sofa object in the DB. How can I do this with a many-to-many relationship? The traditional many-to-many has a unique-constraint that prevents this.
I'm thinking of using a through table, with another field in the unique-constraint (date-purchased, or just a random string). Will that work?
Does anyone have a better way of doing this?
Creating a through table is a good approach here, as everything will be DRY and pretty easy to use:
class Ownership:
owner = models.ForeignKey(Person, on_delete=models.CASCADE)
furniture = models.ForeignKey(Furniture, on_delete=models.CASCADE)
items_owned = models.IntegerField(default=0)
class Meta:
unique_together = [
'owner', 'furniture'
]
person = Person.objects.get(name='Bob')
furniture = Furniture.objects.get(furniture_name='Sofa')
# update the number of items owned
Ownership.objects.update_or_create(
owner=person, furniture=furniture, defaults=dict(items_owned=3))
# get the number of items owned
person.ownership_set.get(furniture=furniture).items_owned
# or for example
Ownership.objects.get(owner__name='Bob', furniture__name='Sofa').items_owned
You can then abstract away this complex querying and updating logic with custom managers https://docs.djangoproject.com/en/2.0/topics/db/managers/#custom-managers
Why do we use an intermediate model?
Can't we just use Many to many relationship without intermediate model?
M2M relationships require intermediate tables. You can read more about what M2M relationships are and why they require an intermediate table (referred to as a junction table in the article) here:
Django abstracts this away by automagically creating this intermediate table for you, unless you need to add custom fields on it. If you do, then you can define it by overriding the through parameter as shown here
Here's a quick picture of why the table is required
Source: https://www.geeksforgeeks.org/intermediate-fields-in-django-python/
Let's say you have two models which have a Many-to-Many relationship, like Customer and Product. One customer can buy many products and a product can be bought by many customers.
But you can have some data that doesn't belong to neither of them, but are important to the transaction, like: quantity or date.
Quantity and date are the intermediary data which are stored in intermediary models.
from django.db import models
class Item(models.Model):
name = models.CharField(max_length = 128)
price = models.DecimalField(max_digits = 5, decimal_places = 2)
def __str__(self):
return self.name
class Customer(models.Model):
name = models.CharField(max_length = 128)
age = models.IntegerField()
items_purchased = models.ManyToManyField(Item, through = 'Purchase')
def __str__(self):
return self.name
class Purchase(models.Model):
item = models.ForeignKey(Item, on_delete = models.CASCADE)
customer = models.ForeignKey(Customer, on_delete = models.CASCADE)
date_purchased = models.DateField()
quantity_purchased = models.IntegerField()
When you buy a product, you do it through the Purchase model: the client customer buys quantity_purchased quantity of items item in date_purchased.
The Purchase model is the Intermediate model.
Django documentation says:
...if you want to manually specify the intermediary table, you can use
the through option to specify the Django model that represents the
intermediate table that you want to use.
In this case we have this line in the Customer model, which defines the intermediary model in through = 'Purchase'
items_purchased = models.ManyToManyField(Item, through = 'Purchase')
Let's now use the example from the Django Documentation.
You have a database of musicians with a Many-to-Many relationship with the bands the belong to: a musician can belong can be part of many bands, and the bands can have many musicians.
What data do you want to keep?
For musicians (person): name and instrument they play
For the bands: name and style.
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=128)
age = models.IntegerField()
class Group(models.Model):
name = models.CharField(max_length=128)
style = models.CharField(max_length=128)
person = models.ForeignKey(Person, on_delete=models.CASCADE)
But, wouldn't you think that knowing when the person joined the band is important? What model would be the natural place to add a date_joined field? It makes no sense to add it to Person or Group, because it's not an intrinsic field for each of them, but it's related to an action: joining the band.
So you make a small, but important adjustment. You create an intermediate model that will relate the Person, the Group with the Membership status (which includes the date_joined).
The new version is like this:
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=128)
age = models.IntegerField()
class Group(models.Model):
name = models.CharField(max_length=128)
style = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
class Membership(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
date_joined = models.DateField()
The changes are:
You added a new class called Membership which reflects the membership status.
In the Group model you added members = models.ManyToManyField(Person, through='Membership'). With this you relate Person and Group with Membership, thanks to through.
Something important to clarify.
An intermediate model, or in relational database terms, an associative entity, are always needed in a Many-to-Many (M2M) relationship.
A relational database requires the implementation of a base relation
(or base table) to resolve many-to-many relationships. A base relation
representing this kind of entity is called, informally, an associative
table... that can contain references to columns from the same or different database tables within the same database.
An associative (or junction) table maps two or more tables together by
referencing the primary keys of each data table. In effect, it
contains a number of foreign keys, each in a many-to-one relationship
from the junction table to the individual data tables. The PK of the
associative table is typically composed of the FK columns themselves. (source)
Django will create the intermediate model, even when you don't explicitly define it with through.
Behind the scenes, Django creates an intermediary join table to
represent the many-to-many relationship. By default, this table name
is generated using the name of the many-to-many field and the name of
the table for the model that contains it.
Django will automatically generate a table to manage many-to-many
relationships. However, if you want to manually specify the
intermediary table, you can use the through option to specify the
Django model that represents the intermediate table that you want to
use.
The most common use for this option is when you want to associate extra data with a many-to-many relationship.(source)
I'm working on a Django project, where I have amongst others, two models that have a relationship.
The first model describes a dish in general. It has a name and some other basic information, for instance:
dish(models.Model):
name = models.CharField(max_length=100)
short_desc = models.CharField(max_lenght=255)
vegetarian = models.BooleanField(default=False)
vegan = models.BooleanField(default=False)
The second model is related to the dish, I assume in form of a one-to-one relationship. This model contains the preparation and the ingredients. This data may change over time for the dish (e.g. preparation text is adjusted). Old versions of this text are still stored, but not connected to the dish. So the dish gets a new field, which points to the current preparation text.
preparation = models.???(???)
So, whenever the preparation description is changed a new entry is created for the preparation and the dish's reference to the preparation is updated.
The preparation itself looks like this:
preparation(models.Model):
prep_test = models.TextField()
ingredients = models.TextField()
last_update = models.DateTimeField()
As stated before, I believe that a one-to-one relation would be reasonable between the dish and the preparation.
Is my assumption with the one-to-one relation correct and if so, how do I correctly define it?
If you have multiple preparations for the dish, you don't have a one-to-one relationship by definition.
The way to define this is a ForeignKey from Preparation to Dish. (Note, Python style is that classes start with an upper case letter.)
class Preparation(models.Model):
...
dish = models.ForeignKey('Dish')
Now you can do my_dish.preparation_set.latest('last_update') to get the latest preparation for a dish. If you add an inner Meta class to Preparation and define get_latest_by = 'last_update'), you can leave out the parameter to the latest() call.
Make sure, relations are correct otherwise you have repeating tuples in your models which is not very good practice, make your database very heavy. see relation from my perspective.
class dish(models.Model):
name = models.CharField(max_length=100)
short_desc = models.CharField(max_lenght=255)
vegetarian = models.BooleanField(default=False)
vegan = models.BooleanField(default=False)
class Ingredients(models.Model):
name = models.CharField(max_length=100)
dish = models.ForeignKey(dish)
class preparation(models.Model):
prep_test = models.TextField()
last_update = models.DateTimeField()
dish = models.OneToOneField(dish)
why you don't make one2many relation of dish with preparation.
I dish have multiple preparation but have only one active. you can attach latest on base of last_update = models.DateTimeField()
your model will be like:
class preparation(models.Model):
dish = models.ForeignKey(dish)
...
I would like to be able to let users create locations "on-the-fly" when they create a report. Using the following models if possible.
models:
class Report(models.Model):
...
location = forms.ManyToManyField(Location)
class Location(models.Model):
name = forms.CharField(max_length=255)
...
The behavior I am searching for is close to the one given by the django formsets.
Indeed I am currently able to somewhat do that using instead a foreignkey relationship and relying on formsets. Using this technic,django-extra-views and django-dynamic-formset, I am even able to let users choose how many locations they want to add.
A strip down version of the actual setup :
models:
class Report(models.Model):
...
class Location(models.Model):
name = forms.CharField(max_length=255)
report = forms.ForeignKey('Report')
forms:
class LocationForm(forms.ModelForm):
class Meta:
model = Location
fields = '__all__'
class LocationFormSet(extra_views.InlineFormSet):
model = TestLocation
form_class = TestLocationForm
views:
class ReportCreateView(extra_views.CreateWithInlinesView):
model = Report
inlines = [LocationFormSet, ]
Unfortunately using this setup, a location can not belong to more than one report which bring a lot of duplicated location. That's why I think the ManyToMany relationship would be better suited. I stumbled upon some answers related to this question that talked about intermediary model but I wasn't able to go anywhere on my own to make it work.
Is their a way to tweak my working setup to make it work with a manytomany relationship? Or does it exists a completely different approach to let user create "on-the-fly" the locations during the report creation?
Thank's!
For create "on-the-fly" I had changed ManyToMany by ForeingKey.
class Report(models.Model):
...
location = forms.ManyToManyField(Location) # REMOVE
class Location(models.Model):
report = models.ForeignKey(Report) # ADD
name = forms.CharField(max_length=255)
...
And then, InlineForms will work.
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.