Django models - Quantity field - django

I'm using Django to create a part inventory where I work. Here is a snippet of the models I have:
class Part(models.Model):
description = models.CharField(max_length=64)
number= models.CharField(max_length=64)
price= models.FloatField()
class Group(models.Model):
name = models.CharField(max_length=64)
parts = models.ManyToManyField(Part)
So I have different groups (orders) with some parts in it.
What I want to do is to have a quantity property for the parts of my group. But if I want to add the quantity field to my Part object, each group will have the same quantity, which is not the correct behavior. How can I have my groups remember how much of each part they have?
Thanks for your input and I hope this is not a total noob question!

You would need a through table:
The intermediate model is associated with the ManyToManyField using the through argument to point to the model that will act as an intermediary.
class Part(models.Model):
description = models.CharField(max_length=64)
number= models.CharField(max_length=64)
price= models.FloatField()
class Group(models.Model):
name = models.CharField(max_length=64)
parts = models.ManyToManyField(Part, through='GroupPart')

Create a new through table between Group and Part that holds the quantity.

Related

How to avoid repetition in Django model fields?

I have models that share many common fields. For example:
class Customer(models.Model):
name = models.CharField()
email = models.CharField()
address = models.CharField()
phone = models.CharField()
city = models.CharField()
state = models.CharField()
country = models.CharField()
wallet = models.FloatField()
class Seller(models.Model):
# same fields from Customer class, except the field wallet
To avoid repeating these fields, I have tried to create classes with these common fields and link using OneToOneField:
class ContactInformation(models.Model):
phone = models.CharField()
email = models.CharField()
class AddressInformation(models.Model):
address = models.CharField()
city = models.CharField()
state = models.CharField()
country = models.CharField()
class Customer(models.Model):
wallet = models.FloatField()
contact_information = models.OneToOneField(ContactInformation)
address_information = models.OneToOneField(AddresssInformation)
class Seller(models.Model):
contact_information = models.OneToOneField(ContactInformation)
address_information = models.OneToOneField(AddresssInformation)
But now it gets very messy if I try to create a ModelForm based on the Customer, as there is only the wallet field in it. To display my other OneToOneFields I have to create multiple forms: a form for the contact information and another for address information, as ModelForms don't simply display these OneToOneFields as a single form. The views get bloated, as I have to validate 3 forms in total and have to manually create the object instances.
Am I missing something here? Should I use inheritance instead? Should I just repeat these fields to have simpler forms and views? Any advice would be greatly appreciated.
Take a look at abstract base classes, it provides a clean way to reuse common fields to multiple tables.
You might consider:
from django.db import models
class CommonUserInfo(models.model)
name = models.CharField()
email = models.CharField()
address = models.CharField()
phone = models.CharField()
city = models.CharField()
state = models.CharField()
country = models.CharField()
class Meta:
abstract = True
class Customer(CommonUserInfo):
wallet = models.FloatField()
class Seller(CommonUserInfo):
pass
I am not sure what the benefit of using a foreign key for address information is unless you have multiple customers/sellers using the same address and the addresses will need to be updated in unison.

Django model design issue with relationship

I have an problem where I can’t decide how to design the models for the following scenario
I want to create a companies table that will hold a list of companies. This table will have a comment field in it
I want that comment field to be able to hold multiple comments that are dated
A company can have multiple comments but a comment can only belong to only one company
Here the Comments table
class Comments(model.Models):
date = models.DateField(auto_now_add=True)
comment_text = models.TextField(required=True)
If I create the Companies table like this;
class Companies(models.Model):
name = models.CharField(max_length=30)
country = models.CharField(max_length=30)
comment = models.ForeignKey(Comments, on_delete=models.SET_NULL, null=True)
Then I can only attach one comment to one specific row of the Companies table
If I create the Companies table like this;
class Companies(models.Model):
name = models.CharField(max_length=30)
country = models.CharField(max_length=30)
comment = models.ManyToManyField(Comments)
Then a comment can belong to multiple companies which I don’t want.
In the Django documentation https://docs.djangoproject.com/en/2.0/topics/db/examples/ there is only one other options left which is the one-to-one mapping and this is clearly not what I want.
How can achieve what I want ?
You should do this.
class Company(models.Model):
name = models.CharField(max_length=30)
country = models.CharField(max_length=30)
class Comment(models.Model):
company = models.ForeignKey(Company, related_name='comments', on_delete=models.CASCADE)
date = models.DateField(auto_now_add=True)
comment_text = models.TextField()
When accessing comments of a certain company, It will be;
comments = company.comments.all()
Just put the ForeignKey on Comment, pointing to Company. This does exactly what you want.

Understanding ManyToMany fields in Django with a through model

I'm having trouble understanding the use of ManyToMany models fields with a through model. I can easily achieve the same without the ManyToMany field. Considering the following from Django's docs:
class Person(models.Model):
name = models.CharField(max_length=128)
class Group(models.Model):
name = 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()
invite_reason = models.CharField(max_length=64)
What I don't understand, is how is using the ManyToMany field better than simply dropping it and using the related manager. For instance, the two models will change to the following:
class Group(models.Model):
name = models.CharField(max_length=128)
class Membership(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE, related_name='members')
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)
What am I missing here?
You're right, if you define the membership table explicitly then you don't need to use a ManyToManyField.
The only real advantage to having it is if you'd find the related manager convenient. That is, this:
group.members.all() # Persons in the group
looks nicer than this:
Person.objects.filter(membership_set__group=group) # Persons in the group
In practice, I think the main reason for having both is that often people start with a plain ManyToManyField; realize they need some additional data and add the table explicitly; and then continue to use the existing manager because it's convenient.
So I just wanted to add to anyone who is looking at this and may want another example to save them research. For one, I think it's important to note that in OP's questions, he should of removed the Group model not the People model and removed the matching field from the Membership model. That way, the model goes back to it's original meaning.
When looking at a many-to-many relationship, the through field can almost be contrived as the "why" to the many-to-many relationship. If we give the nomenclature a different name, it might change what the reader sees:
class Person(models.Model):
name = models.CharField(max_length=128)
class Club(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='RegistrationReceipt')
class RegistrationReceipt(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
club = models.ForeignKey(Club, on_delete=models.CASCADE)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)
paid_dues = models.BooleanField(default = True)
fee_payment_date = models.DateTimeField()
Now, you can imagine yourself adding all sorts of logic whenever a member joins this club. When they joined? Why did they join? Did they pay? When is their payment date? etc. You can obviously tackle this relationship in different ways, but you can see more clearly the use of "through" in a Many-to-Many relationship.
Also, for those that know SQL. The through attribute/field is the way you customize the intermediary table, the one that Django creates itself, that one is what the through field is changing.
I have some problem with the answer from Kevin Christopher Henry.
I don't think that the equivalent of the group.members.all() without a through="members" is Person.objects.....
Instead I think it is group.person_set.all() if the M2M field is on Person side. Or group.persons.all() if the M2M field is inside Group.
But I think without through=.. you have no control over the created table. It contains and will contain just 2 fields: both ID's of the related rows.
But with through=.. you can create the model yourself and add (now or later) the additional fields, which often can have a good reason. Example of such field: valid_from = DateField(), or so.

Django - get object based on ForeignKey field

I need to get a single model object based on a set of fields one of which is a ForeignKey. So if I have the following models:
class Composer(models.Model):
name = models.CharField()
dates = models.CharField()
nationality = models.CharField()
period = models.CharField()
class Symphony(models.Model):
composer = models.ForeignKey('Composer', related_name="symphonies", null=True)
date = models.CharField()
key = models.CharField()
number = models.IntegerField()
num_movements = models.IntegerField()
how can I then retrieve a specific Symphony object based on its composer and number fields? I originally had composer as a simple models.CharField(), so I could just do:
Symphony.objects.get(composer='Bruckner', number=7)
So how do I do the equivalent using a ForeignKey?
This should work, but you should always use a unique value:
Symphony.objects.get(composer__name='Bruckner', number=7)
Here you can find more usages for double underscore notation (__)

django implementation of models

I have to implement two models in a django project --> Country and Continent.
It is possible to import all information about countries and continents from an XML file that populates the database with these two tables.
I've implemented them in this way
from django.db import models
class Continent(models.Model):
name = models.CharField(max_length=255, unique=True)
code = models.CharField(max_length=255, unique=True)
countries = ?
class Meta:
ordering = ['name']
class Country(models.Model):
name = models.CharField(max_length=255)
capital = models.CharField(max_length=255)
code = models.CharField(max_length=255, unique=True)
population = models.IntegerField(default=0)
area = models.IntegerField(default=0)
continent = models.ForeignKey(Continent)
class Meta:
ordering = ['name']
You can see a ? mark in Continent.countries because I don't understand how to implement it. The problem is: "Countries of a continent should be accessible through attribute countries of class Continent"
This means that it is possible to do this:
from MyApplication.model import Country, Continent
europe = Continent.object.get(code=u'eu')
finland = europe.countries.get(code=u'fi')
I've tried models.ForeignKey, but it doesn't work.
It says that "Nonetype object doesn't have any get method"
Can anyone help me please?
Thanks
Django does not support one to many directly, instead you can use the reverse of one-to-one relation to get a one-to-many relation.
So, go ahead with the other approach of using many to one by adding foreign key inside the country relating to continent. Now, you have a one-to-one from country to continent. Next, do a reverse query from Continent's object to get the desired relation. Take a look:
class Continent(models.Model):
...
class Country(models.Model):
...
continent = models.ForeignKey(Continent, related_name="countries")
...
europe = Continent.object.get(code=u'eu')
finland = europe.countries.get(code=u'fi')