Foreign Key Django Model - python

I'm trying to create 3 models ; Person, Address and Anniversy. The plan is to have one address and one anniversy for each person. But each address and anniversy can have multiple persons.
So far I have the following, but I think the OneToMany(foreign key) relationships maybe the wrong way round. i.e each address can have one person but each person can have multiple addresses.
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=50)
birthday = models.DateField()
def __unicode__(self):
return u'%s' % (self.name)
class Address(models.Model):
person = models.ForeignKey(Person)
address = models.CharField(max_length=150)
def __unicode__(self):
return u'%s' % (self.address)
class Anniversy(models.Model):
person = models.ForeignKey(Person)
anniversy = models.DateField()
def __unicode__(self):
return u'%s' % (self.anniversy)

You create the relationships the other way around; add foreign keys to the Person type to create a Many-to-One relationship:
class Person(models.Model):
name = models.CharField(max_length=50)
birthday = models.DateField()
anniversary = models.ForeignKey(
"Anniversary", on_delete=models.CASCADE)
address = models.ForeignKey(
"Address", on_delete=models.CASCADE)
class Address(models.Model):
line1 = models.CharField(max_length=150)
line2 = models.CharField(max_length=150)
postalcode = models.CharField(max_length=10)
city = models.CharField(max_length=150)
country = models.CharField(max_length=150)
class Anniversary(models.Model):
date = models.DateField()
I used string names for the other models so they can still be defined after, or you can define the Person model last.
Any one person can only be connected to one address and one anniversary, but addresses and anniversaries can be referenced from multiple Person entries.
Anniversary and Address objects will be given a reverse, backwards relationship too; by default it'll be called person_set but you can configure a different name if you need to. See Following relationships "backward" in the queries documentation.

I would advise, it is slightly better practise to use string model references for ForeignKey relationships if utilising an app based approach to seperation of logical concerns .
So, expanding on Martijn Pieters' answer:
class Person(models.Model):
name = models.CharField(max_length=50)
birthday = models.DateField()
anniversary = models.ForeignKey(
'app_label.Anniversary', on_delete=models.CASCADE)
address = models.ForeignKey(
'app_label.Address', on_delete=models.CASCADE)
class Address(models.Model):
line1 = models.CharField(max_length=150)
line2 = models.CharField(max_length=150)
postalcode = models.CharField(max_length=10)
city = models.CharField(max_length=150)
country = models.CharField(max_length=150)
class Anniversary(models.Model):
date = models.DateField()

Related

How to add all foreign key relations in Django?

I have these two models :
class Country(models.Model):
Name = models.CharField(max_length=100)
Population = models.CharField(max_length=100)
Language = models.IntegerField()
Total_persons= models.IntegerField()
class Person(models.Model):
Country = models.ForeignKey(Country, on_delete=models.CASCADE, null=True)
name = models.CharField(max_length=100)
contact = models.IntegerField()
In Country I have USA, CANADA, MEXICO, SPAIN, FRANCE......
In Person I have more than 1000 persons.
How can I make the Total_persons increase as the Total number of Persons increase in that country.
Instead of making Total_persons a separate field, you can drop the field and simply get the number of Persons belonging to a Country instance country with:
country.person_set.count()
Or if you prefer a friendlier name, you can give the Country foreign key in Person a related_name:
class Person(models.Model):
Country = models.ForeignKey(Country, related_name='persons', on_delete=models.CASCADE, null=True)
so that you can get the same count with:
country.persons.count()
Or if you'd still prefer to make Total_persons a separate field, you can override the Person.save() method to sync its country when a Person instance is created (when its pk is None), and override the Person.delete() method to sync when Person instance is deleted:
class Person(models.Model):
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
if not self.pk:
self.Country.Total_persons = self.Country.person_set.count()
self.Country.save()
def delete(self):
country = self.Country
super().delete()
country.Total_persons = country.person_set.count()
country.save()
Your model field names should be lower case.
See Coding Style-Model for more information. Also your population and language fields don't seem to have the right data type??
Another way to achieve what you need is by using model properties.
class Country(models.Model):
name = models.CharField(max_length=100)
population = models.CharField(max_length=100)
language = models.IntegerField()
#property
def total_people(self):
return self.people.count()
class Person(models.Model):
Country = models.ForeignKey(Country, on_delete=models.CASCADE, null=True,
related_name='people')
name = models.CharField(max_length=100)
contact = models.IntegerField()
You have several ways to have the number of people in countries without a DB field:
With annotations:
qs = Country.objects.annotate(total_people=models.Count('person_set'))
for country in qs:
print(country.total_people)
Else if you really want to have a DB field, you can use post/pre hooks.
There are:
Post save
#receiver(post_save, sender=Person)
def increment_total_people(sender, instance, created, **kwargs):
instance.Country.Total_persons += 1
instance.Country.save()
Post delete
#receiver(post_delete, sender=Person)
def decrement_total_people(sender, instance, **kwargs):
instance.Country.Total_persons -= 1
instance.Country.save()
But you'll have to design things for all use cases when this number could change.

Create additional fields in models on foreign key (Django query)

models.py
from django.db import models
class person(models.Model):
name = models.CharField(max_length=100, blank=true)
characteristics = models.ManyToManyField(characteristics, default = '', on_delete=models.SET_DEFAULT)
def __str__(self):
return self.name
class characteristics(models.Model):
name = models.CharField(max_length=100, blank=true)
def __str__(self):
return self.name
I have some troubles with queryset.
I create 3 persons: Jim, John, Jack.
Then create 4 characteristics: strength, agility, wisdom, intelligence
Connect Jim with strength and agility( he is strong and dexterous)
Connect John with wisdom and intelligence( he is a smart guy)
Connect Jack with strength, agility, wisdom, intelligence( he is jack of all trades)
Output will be like:
Jim: strength, agility
John: wisdom, intelligence
Jack: strength, agility, wisdom, intelligence
Is there a posibility to add values to these characteristics?
For example:
Jim: strength=10 , agility=10
John: wisdom=10, intelligence=10
Jack: strength=9, agility=8, wisdom=8, intelligence=9
I think, this values need to be stored in "person" table. I can create additional fields in models.py, but my goal is to make additional field in "person" model when creating new charactaristics.
Thereis a way to make it using 0...1000 empty int fields in "person" model, then giving additional number(like id) to charecteristics and then connect that characteristics id with "person" 0...1000 empty fields
from django.db import models
class person(models.Model):
name = models.CharField(max_length=100, blank=true)
characteristics = models.ManyToManyField(characteristics, default = '', on_delete=models.SET_DEFAULT)
char0 = IntegerField(blank=True)
...
char1000 = IntegerField(blank=True)
def __str__(self):
return self.name
class characteristics(models.Model):
name = models.CharField(max_length=100, blank=true)
additional_id = models.BigAutoField
def __str__(self):
return self.name
Is there some elegant way to solve my problem?
I think you need to try something like this:
class characteristics(models.Model):
id = models.AutoField(primary_key=True, )
name = models.CharField(max_length=100)
parameter = models.SmallIntegerField(default=0)
Next you can do:
Jim = person.objects.get(name = Jim)
jims_str = characteristics.objects.create(name = "strengh", parameter = 5)
jims_wisdom = characteristics.objects.create(name = "wisdom", parameter = 2)
Jim.characteristics.add(jims_str)
Jim.characteristics.add(jims_wisdom)
Jim.save()
I can miss some details, but the main logic might be like that. Different Characteristics (the good practice is to name class names with Capital Latter in Python) might have same names, but unique PrimaryKey, and will be connected to Person model via M2M relation.
METHOD 2:
You can edit your Person like:
class Person(models.Model):
id = models.AutoField(primary_key=True, )
name = models.CharField(max_length=100)
strength = models.ForeignKey('Strength', blank=True, null=True, on_delete=models.SET_NULL)
agility= models.ForeignKey('Agility', blank=True, null=True, on_delete=models.SET_NULL)
intelligence= models.ForeignKey('Intelligence', blank=True, null=True, on_delete=models.SET_NULL)
wisdom= models.ForeignKey('Wisdom', blank=True, null=True, on_delete=models.SET_NULL)
Next create 4 models: Strength, Agility, Wisdom, Intelligence
class Strength(models.Model):
id = models.AutoField(primary_key=True, )
parameter = models.SmallIntegerField(default=0)
Found 4 solutions.
1st(i ought to use it coz haven't got enough time and haven't found other propriate way):
models.py
from django.db import models
class person(models.Model):
name = models.CharField(max_length=128, blank=False)
characteristics = models.ManyToManyField(characteristics,
null=True,
on_delete=models.SET_NULL)
characteristics1 = models.FloatField(blank=True, null=True)
....
characteristics20 = models.FloatField(blank=True, null=True)
class characteristics(models.Model):
name = models.CharField(max_length=100, blank=true)
additional_id = models.BigAutoField
After creating new object in characteristics, object will get additional_id(starts from 1).
Lateron connect characteristics with person.characteristicsX, where (X = characteristics.additional_id).
Then, we can call for object, and sort characteristics by characteristicsX(where X = characteristics.additional_id)
2nd:
models.py
from django.db import models
class person(models.Model):
name = models.CharField(max_length=128, blank=False)
characteristics = models.ManyToManyField(characteristics,
null=True,
on_delete=models.SET_NULL)
characteristics1_type = models.ForeignKey('characteristics', blank=True, null=True, on_delete=models.SET_NULL)
characteristics1_value = models.FloatField(blank=True, null=True)
....
characteristics20_type = models.ForeignKey('characteristics', blank=True, null=True, on_delete=models.SET_NULL)
characteristics20_value = models.FloatField(blank=True, null=True)
class characteristics(models.Model):
name = models.CharField(max_length=100, blank=True)
May cause troubles, because user can set 2 identical characteristics. Still there is a way to prohibit it using forms or etc.
3rd:
models.py
from django.db import models
class person(models.Model):
name = models.CharField(max_length=128, blank=False)
characteristics = models.models.TextField(blank=True)
class characteristics(models.Model):
name = models.CharField(max_length=100, blank=True)
Then save values into person.characteristics field using forms. This way can still work, but then it requiers to sort the information from TextField by charecteristics.objects. It'll take some time to setting it well, but will work awesome.
4th:
Use postgreSQL and store an array.

OneToThree django model

I have a classic model containing some contact data (mail, addr, names, ...), and an other which is supposed to link to three contacts (an admin, an owner, a tech). A contact can be registered as admin and as tech for example.
class Contact(django.db.models.Model):
user = models.OneToOneField(User)
city = models.CharField(max_length=255, blank=True)
country = models.CharField(max_length=2, blank=True)
email = models.EmailField(max_length=255, blank=True)
first_name = models.CharField(max_length=255, blank=True)
family_name = models.CharField(max_length=255, blank=True)
phone = models.CharField(max_length=255, blank=True)
class Product(... Some parents)
name = models.CharField(max_length=255)
contacts ?
I don't know how to link 3 contacts to my other Model ... and generate a form with a queryset already existing.
Some advices ?
Thanks
just use foreign keys
example:
class Product(... Some parents)
name = models.CharField(max_length=255)
admin = models.ForeignKey(Contact, related_name="admins")
owner = models.ForeignKey(Contact, related_name="owners")
tech = models.ForeignKey(Contact, related_name="techs")
if you want to generate the form use a modelform
class MyForm(forms.ModelForm):
class Meta:
model=Product
the select widgets will be filled automatically
EDIT: to display your contact in a human friendly way use
class Contact(django.db.models.Model):
<usual stuff>
def __unicode__(self):
return u"%s" % self.firstname # or any field you wish
see https://docs.djangoproject.com/en/1.10/ref/models/instances/#str
if you use python 3 replace unicode by str

Models in Python Django not working for Many to Many relationships

I am trying to create the proper Django model that could fit the following reqs:
Person Class has 1 to many relations with the Address Class
Person Class has many to many relations with the Group Class
Book Class contains the collections of the Persons and the Groups
This is my code:
class Person(models.Model):
first_name = models.CharField(max_length=15)
last_name = models.CharField(max_length=20)
def __str__(self):
return self.first_name+ ' - ' + self.last_name
class Address(models.Model):
person = models.ForeignKey(Person)
address_line = models.CharField(max_length=200)
def __str__(self):
return self.address_line
class Group(models.Model):
group_name = models.CharField(max_length=12)
persons = models.ManyToManyField(Person)
def __str__(self):
return self.group_name
class Book(models.Model):
record_name = models.CharField(max_length=12)
person = models.ForeignKey(Person )
group = models.ForeignKey(Group )
def __str__(self):
return self.record_name
However it's not correct:
1) A Group can now contain multiple Persons but the Persons do not contain any Group.
I am not sure if I should add to the Person class the following code:
groups = models.ManyToManyField(Group)
2) The Book class now contains only 1 record of Person & Group per Book record.
3) When I added the Foreign Keys to the models, I removed
on_delete tag:
person = models.ForeignKey(Person, on_delete=models.CASCADE())
because it does not compile it, asking for some params.
I know how to make all this for C#, but I am a kinda stucked with this simple task in Python/Django.
1) The ManyToMany field should appear only in one of the models, and by looks of things you probably want it in the Person model.
Its important to understand that the data about the ManyToMany field is saved in a differant table. Django only allows this field to be visable through buth models (so basiclly, choose where it is move convinient).
2)By the look of your structure I will suggest you use a ManyToMany field through a different table. here is an example:
class Activity(models.Model):
name = models.CharField(max_length=140)
description = models.TextField(blank=True, null=True)
class Route(models.Model):
title = models.CharField(max_length=140)
description = models.TextField()
activities_meta = models.ManyToManyField(Activity, through = 'RouteOrdering')
class RouteOrdering(models.Model):
route = models.ForeignKey(Route, on_delete=models.CASCADE)
activity = models.ForeignKey(Activity, on_delete=models.CASCADE, related_name='activita')
day = models.IntegerField()
order = models.IntegerField(default=0)
that way the data is binded to the ManyToMany field

Create a MultipleChoiceField using rows from another model,

I am creating an online job application for a company with multiple locations. I would like to allow the applicant to be able to select checkboxes that represent every store they would like to apply to (could be multiple). I am hesitant to hard code these for scalability purposes, so I had hoped to create 2 models (I have more than that, but for this example these are the only 2 that are relevant):
Applicant
class Applicant(models.Model):
name = models.CharField(max_length=200)
city = models.CharField(max_length=200)
state = models.CharField(max_length=200)
zip = models.CharField(max_length=200)
social_security_number = models.CharField(max_length=200)
phone = models.CharField(max_length=200)
alt_phone = models.CharField(max_length=200, blank=True)
us_citizen = models.BooleanField()
committed_felony = models.BooleanField()
is_16 = models.BooleanField()
has_drivers_license = models.BooleanField()
is_disabled = models.BooleanField()
prev_employed = models.BooleanField()
felony_explanation = models.TextField(blank=True)
disabled_explanation = models.TextField(blank=True)
prev_employment_manager = models.CharField(max_length=200, blank=True)
prev_employment_year = models.CharField(max_length=4, blank=True)
skills = models.TextField()
was_completed = models.BooleanField(default=False)
def __unicode__(self):
return self.name
Store
class Store(models.Model):
code = models.CharField(max_length=10)
description = models.CharField(max_length=200)
city = models.CharField(max_length=20)
state = models.CharField(max_length=20)
def __unicode__(self):
return self.description
I would (I think) like to add a MultipleChoiceField in the applicant model, that creates choices from all of the instances of Store (one for each row). So far, I have tried this in the applicant class:
def get_stores():
self.stores = Store.objects.all()
but was unable to (as far as I can tell) grab the instances of Store like I had hoped. Here are a few questions I have:
Is it even possible to reference another model like that?
Is referencing the Store model from the Applicant model the right beginning for creating several checkboxes to let an applicant select all of the stores they are applying to (and allow the list to change dynamically)?
is a MultipleSelectField the best way to do this once I have pulled all of the Store instances?
This seems like the canonical use case for ManyToManyField.
class Store(models.Model):
...
class Applicant(models.Model):
name = models.CharField(max_length=200)
...
was_completed = models.BooleanField(default=False)
stores = ManyToManyField(Store, related_name='applicants')
When you display this form in a field, it should automatically use a MultipleSelectField
REF: https://docs.djangoproject.com/en/dev/topics/db/examples/many_to_many/

Categories