Correct way to access related objects - python

I have the following models
class Person(models.Model):
name = models.CharField(max_length=100)
class Employee(Person):
job = model.Charfield(max_length=200)
class PhoneNumber(models.Model):
person = models.ForeignKey(Person)
How do I access the PhoneNumbers associated with an employee if I have the employee id?
Currently I am using
phones = PhoneNumbers.objects.filter(person__id=employee.id)
and it works only because I know that the employee.id and person.id are the same value, but I am sure this is the incorrect way to do it.
Thanks
Andrew

You can (and should) filter without knowing the foreign key field:
PhoneNumber.objects.filter(employee=your_employee).all()

You could do:
employees = Employee.objects.filter(id=your_id).select_related()
if employees.count() == 1:
phone_numbers = employees[0].phonenumber_set.all()
That should get you all your phone numbers in one db query.
By default you can access models related through a foreignkey on the "opposite" side by using "model name in all lower case" followed by "_set". You can change the name of that accessor by setting the related name property of the foreignkey.

Related

Django ForeignKey Which field?

I have just started learning Django and one thing in models about ForeignKey was unclear to me.
So lets say I have one model like this:
class Webpage(models.Model):
name = models.CharField(max_length=264, unique=True)
url = models.URLField(unique=True)
name2 = models.CharField(max_length=264, unique=True)
class Records(models.Model):
site = models.ForeignKey(Webpage)
So when creating Records entry for testing I see ForeignKey is referenced to Name field of Webpage. My confusion is why exactly name? As I know ForeignKey is referencing to primary key and if you are not giving primary_key attribute to any fields it will create 'id' field and make that as primary_key. So then why not 'id' field but 'name'.
Sorry if this is repeat question, I just couldn`t find answer.
#Orkhan Rustamli
If you are try to list all yours records and get the name of webpage I think it's because your print method on webpage class is set to return the name of attribute.
if you want to return the id you will set the return function like:
class webpage(models.Model):
pass
def str(self):
return self.id

Django: avoid data type limitation

I have a class which has an IntegerField attr.
In its ModelForm, in the field of this attr, I need to send a String, which later will store an Integer once it is processed in the view.
The problem is that Django doesn't allows this, and when I use form.cleaned_data.get('myattr') it's saying that it's wrong because it should be an Integer.
class Student(models.Model):
teacher = models.IntegerField(null=False) # I'm saving teacher's key here
class Teacher(models.Model:
name = models.CharField(max_length= 50, null=False)
key = models.IntegerField(null=False)
class StudentForm(forms.ModelForm):
teacher = forms.ModelChoiceField(queryset=Teacher.objects.all(), label='Select the teacher')
So, when the user is selecting the student's teacher, that select field will display the name of the teachers available. But in the model it will store their key, which I manage in view.
views.py:
teacher = form.cleaned_data.get('teacher') # it has the name
teacher = Teacher.objects.get(name=teacher).key # getting the key in order to store an Integer, but Django is banning my code before anyway.
How can I handle this without changing the data type of the model?
I even added to_field_name in the form field with the value of the Teachers' key.
One better approach here will be to setup a relationship between your student and teacher(using foregin key).
Depending of the need of your app here is how to do:
If one student can have several teachers and one teacher can have several students : https://docs.djangoproject.com/en/2.1/topics/db/examples/many_to_many/
If one student can have only one teacher but a teacher can have multiple students:
https://docs.djangoproject.com/en/2.1/topics/db/examples/many_to_one/
If one student can only have one teacher, and one teacher can only have one student:
https://docs.djangoproject.com/en/2.1/topics/db/examples/one_to_one/
This is the best way to manage this.
Then you only have to map the Student model in the Student form like:
class StudentForm(forms.ModelForm):
class Meta:
model = Student
#you can add the list of all the fields you want there
fields = ['teacher']
One extra step will be to define the str method of the model so Django will associate a string representation of your model in your form (Here for having a nice way to display Teacher in the student form).
class Teacher(models.Model):
name = models.CharField(max_length= 50, null=False)
#place other fields here ...
def __str__(self):
#if you print a Teacher django will return the string corresponding to the teacher name
return self.name

Django views query and the foreign key lookup

I'm having trouble running a Django query (v1.9) within my views page. I'm basically retrieving one record from the primers table (that's working fine), then trying to retrieve the corresponding record from the 'gene' table:
models.py:
class Gene(models.Model):
GeneName = models.CharField(max_length=10, unique=True)
ChromosomeNo = models.CharField(max_length=2)
class Primer(models.Model):
PrimerName = models.CharField(max_length=20, unique=True)
Gene = models.ForeignKey(Gene)
views.py
def PrimerDetail(request, pk):
primer_info = get_object_or_404(Primer, pk=pk)
Gene_info = Gene.objects.get(Gene_id = primer_info.GeneName)
The problem appears to be with my use of primer_info.GeneName. I get:
'Primer' object has no attribute 'GeneName'
Change it to primer_info.Gene and I get:
Cannot resolve keyword 'Gene_id' into field. Choices are: ChromosomeLoc, ChromosomeNo, Comments, Disease, Disease_id, GeneName, NoExons, id, primer`
I can substitute for a string value and it works fine. How should I reference a field that is a foreign key object in this context?
with the above description you can try out one more way of getting the Gene object this is follows as.
Gene_info = Gene.objects.get(pk = primer_info.Gene.id)
this would give you the object of Gene object that is foreign key in the Primer Table.
Well it's because you never have a field called Gene_id in your Primer model. Since Gene is the foreign key in Primer, it's easy to get Gene_info:
Gene_info = primer_info.Gene
If you want to query directly on Gene model(which is pretty unnecessary in your case), do:
Gene_info = Gene.objects.get(primer__id=primer_info.id)

Get only one related object from Django ORM

Here are stripped down versions of the models I'm dealing with:
class Contact(models.Model):
contact_no = models.IntegerField(primary_key=True)
email_address = models.CharField(max_length=60, null=True)
class ContactRole(models.Model):
contact_no = models.ForeignKey(Contact, primary_key=True, db_column='contact_no')
role_code = models.CharField(primary_key=True, max_length=16)
role_scope_code = models.CharField(primary_key=True, max_length=16)
Contacts can and almost always do have many ContactRoles.
I want a list of Contacts where the role_scope_code of the related ContactRole is 'foo'. I know I can get this with:
Contact.objects.filter(contactrole__role_scope_code='foo')
What I also want, is for each Contact in the queryset to have a single .contactrole property. It would be the ContactRole with the role_scope_code of 'foo'. Instead I'm getting a set of all ContactRoles that match on contact_no, so that to get to properties of the ContactRole I have to do something like this:
contacts = Contact.objects.filter(contactrole__role_scope_code='foo')
for contact in contacts:
print contact.contactrole_set.filter(role_scope_code='foo')[0].role_code
I have to filter on role_scope_code twice! That doesn't seem DRY at all. What I'm looking for is a query that will allow me to have a set that works like this:
contacts = Contact.objects.filter(contactrole__role_scope_code='foo')
for contact in contacts:
print contact.contactrole.role_code
For the life of me I can't figure out how to tell Django to only return the related objects that match the filter I applied to the parent object.
A OneToOneField will solve this provided that a contact only have one contactrole. A OneToOneField gives you the api you are looking for. So instead of using a ForeignKey use a OneToOneField

Validating admin site save in Django

I have the following two classes defined in models.py
class Part(models.Model):
name = models.CharField(max_length=32)
value = models.CharField(max_length=32)
class Car(models.Model):
name = models.CharField(max_length=32)
parts = models.ManyToManyField(Part)
So my model allows for multiple parts, but I want the part names to be unique. For example, only one part with name "engine" should be allowed for a given car. How do I enforce this uniqueness?
Things I have looked into:
class save()
Overriding the default save() for Car doesn't help because parts isn't updated until save is hit.
class save_model()
This will not work because it always saves.
So what are my options for enforcing uniqueness of part names?
UPDATED:
Although I want only one part with name engine to be associated with a car, I still want the ability to define multiple parts with name engine, of course with different values.
So a car with can have a part (part.name=Engine and part.value=V6) and another car can have a part (part.name=Engine and part.value=V4, but a car can't have two parts that have part.name == engine.
EDIT:
class Part(models.Model):
name = models.CharField(max_length=32)
value = models.CharField(max_length=32)
class CarPart(models.Model):
car = models.ForeignKey(Car)
part_type = models.CharField(max_length=32, unique=true)
part = models.ForeignKey(Part)
class Car(models.Model):
name = models.CharField(max_length=32)
treat part_type as part type id (e.g. type='engine') for engines it will be unique
more about ForeignKey

Categories