query chain of models in Django - django

I have three models for countries, provinces and cities. one of URLs includes the city name where a post should go. I could get the name of the city through the URL . In view function, I could find the city name and then I could find the country which the city is belong to. Now, I want to list all cities under this country. My question is how can I do this where there is no direct relation between city and country. The city is related to the province and the province related to the country. The list I want should includes all cities inside the country regardless of the province. How can I do this? is there any possible solution rather than make direct relation between city and country model?
Note: I could get the country ID through the variable post_city. The country ID for selected city was 3. Therefor, I want all of cities that are under the country that has an ID of 3.
Example: through the URL: I got post_city variable as Los Angeles. Hence, I want all cities in the USA regardless of state of California. Another example, If the post_city variable was London, then, I want all cities in Britain regardless of the province.
the models I have are as follow:
class Country(models.Model):
country_name = models.CharField(max_length=64, unique=True)
def __str__(self):
return "%s" % (self.country_name)
class Province(models.Model):
country_id = models.ForeignKey(Country, on_delete=models.CASCADE)
province_name = models.CharField(max_length=64)
def __str__(self):
return "%s" % (self.province_name)
class City(models.Model):
province_name = models.ForeignKey(Province, on_delete=models.CASCADE)
city_name = models.CharField(max_length=64)
def Country(self):
return self.province_name.country_id.country_name
the view function as follow:
def list_page(request, post_city):
p_c = City.objects.filter(city_name__iexact=post_city).get()
p_p = p_c.province_name
p_country = p_p.country_id
pp=City.objects.all()
print(pp)
context = {
'post_city' : post_city,
'all_p_cities': all_p_cities,
}
return render(request, 'path/to/list_page.html', context )

You can chain multiple related model lookups by using several __ parts in the lookup argument name.
cities_in_narnia = City.objects.filter(province_name__country_id__country_name='Narnia')
Read more in the Django docs: Lookups that span relationships
As a side note. There are some naming conventions you can use that will make your code more readable, especially when seeking help from the online community.
class City(models.Model):
# foreign key fields should be the snake_case or lower case name of the related model class
province = models.ForeignKey(Province, on_delete=models.CASCADE)
# there's no need to prefix field or attribute names with the class name
# City.city_name is superfluous. City.name is perfectly clear.
name = models.CharField(max_length=64)
# methods and attributes should also be snake_case, only use CapitalCase for class names.
#property
def country_name(self):
return self.province.country.name
If you follow these naming conventions, the filter lookup will also be readable and concise.
cities_in_narnia = City.objects.filter(province__country__name='Narnia')

Related

What's a reverse foreignkey and a normal foreignkey

This a question from a Django noob, my question goes thus;
What is the difference between a normal foreignkey and a reverse relation and what is the difference. I always thought;
method 1
class State(models.Model):
name = models.CharField()
class Country(models.Model):
name = models.CharField()
state = models.ForeignKey(State) # normal foreignkey
method 2
class Country(models.Model):
name = models.CharField()
class State(models.Model):
name = models.ForeignKey(Country) # reverse relation
What is the main difference between method 1 and 2 and when to use it.
This is basically a database related question and I encourage you to read further about it.
a Foreign key relationship in Django translates to 1 to many relationship in database design.
In your example and real world 1 state can have 1 country and one country only.
But a Country can have infinite (or n) number of states.
So in our country model we can't have infinite fields to represent all the states.
What we can do is we tell each state that which country they belong to.
so this is the right way to do it here.
class Country(models.Model):
name = models.CharField()
class State(models.Model):
name = models.CharField()
country = models.ForeignKey(Country)
In the first method that you wrote each country can have 1 state but each state can belong to multiple countries.

Django Model Forms with Multi-Level Foreign Keys

I need to create a model form in Django and have the following arbitrary scenario.
Real Estates
============
ID
...some extra fields...
CityID
Cities
======
ID
Name
Region
======
ID
Name
Country
=======
ID
Name
What I would like to do is to let user choose the Country first, then Region and lastly the City. (Populate the child category with javascript after user selects the parent category.) However, I don't want to add the 'Region' and 'Country' fields to the 'Real Estate' table. The order of the fields are also important, that is, 1) Country, 2) Region and 3) City.
Can you suggest any approach to this? Thanks!
I'm assuming you are trying to show the user a limited set of options for "Region" after he selected a country and a limited set of options for "City" after he selected the region so as to provide a way to sensibly select a city for the real estate instead of having to pick something from a long list of random cities?
You could specify additional fields on the RealEstate ModelForm that aren't actual fields on the RealEstate model and provide logic in the form's .__init__() and if needed also in .clean() and .save() methods to take care of the additional fields. Perhaps something like this:
class Country(models.Model):
name = models.CharField(...)
class Region(models.Model):
name = models.CharField(...)
country = models.ForeignKey(Country, on_delete=models.CASCADE)
class City(models.Model):
name = models.CharField(...)
region = models.ForeignKey(Region, on_delete=models.CASCADE)
class RealEstate(models.Model):
name = models.CharField(...)
city = models.ForeignKey(City, on_delete=models.CASCADE)
class RealEstateForm(forms.ModelForm):
country = forms.ChoiceField(required=False)
region = forms.ChoiceField(required=False)
class Meta:
model = RealEstate
fields = ['name', 'city', 'country', 'region']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['country'].choices = Country.objects.all().values('id', 'name)
self.fields['region'].choices = Region.objects.all().values('id', 'name', 'country__id')
self.fields['city'].choices = City.objects.all().values('id', 'name', 'region__id', 'region__country__id')
Providing choices that include the referenced region ID and country ID for each city will allow you to filter these in the front end if you manage to use these choices as the user is selecting country and region without having to make a separate backend request to get the filtered list. Of course, you would end up with a larger response being sent this way in one go - if your data set size is very large (how many countries, how many regions, how many cities) this may not be wanted.

Django query complex query in one to one field model

I have two models of Student and Parent
Student models.py:
class StudentInfo(models.Model):
admissionNumber = models.BigIntegerField(primary_key=True,default=0)
firstName = models.CharField(max_length=20)
lastName = models.CharField(max_length=20)
fullName = models.CharField(max_length=50)
gender = models.CharField(max_length=20)
dob = models.DateField(null=True)
classSection = models.CharField(max_length=20)
Parent models.py
class ParentInfo(models.Model):
student = models.OneToOneField(StudentInfo,primary_key=True, on_delete=models.CASCADE)
fatherName = models.CharField(max_length=20)
motherName = models.CharField(max_length=20)
I have a form to search students through their fatherName.
So, what I want is to filter those students whose father's name contains 'some name'.
I tried this but it resultes in query set of ParentInfo:
parentInfo = ParentInfo.objects.all()
studentsInfo = parentInfo.filter(parent__fName = fName).select_related('student')
You should filter the opposite way, like:
StudentInfo.objects.filter(parentinfo__fatherName='name of father')
You here thus obtain a QuerySet of StudentInfos which contains zero, one, or more StudentInfos where there is a related ParentInfo object where the fatherName field is, in this case 'Name of father'.
Note: It might be better to implement a ForeignKey in the opposite order, such that multiple students can refer to the same ParentInfo object. Right now, a ParentInfo object can refer to exactly one StudentInfo. If there are students with the same parents (so siblings), then you introduce data duplication in the database.
# You can use contains attribute on the field of model and your query can be like this
student = models.ParentInfo.objects.values('student__firstName', 'student__lastName').filter(fatherName__contains='your value')
print(student[0]['student__firstName'])
print(student[0]['student__lastName'])

How to call a a field of one model A into another model B so that b can work as a view

I have created a model called Department, Course. Models are as follow
This is the model for departments and course
class Departments(models.Model):
Department_Id = models.IntegerField(primary_key=True)
Department_Name = models.CharField(max_length=200)
Department_Code = models.CharField(max_length=200)
class Course(models.Model):
Course_Id = models.IntegerField(primary_key=True)
Department_Id = models.ForeignKey(Departments, on_delete=models.CASCADE)
Course_Name = models.CharField(max_length=200)
Course_Code = models.CharField(max_length=200)
I want to create a model called view which can be later on called for search. I want a view model in a such a way that it consit of the data in concat form i.e. name= Department_name+ Course_Name
class View (models.model):
view_id= models.IntegerField(primary_key=True)
Name= Department_name(I want this from Departments table)
+ Course_Name(I want this from Course table)
I try using one to one relation . I would really appricate the help
It's not clear why you'd want to do that. It's never a good idea to duplicate data from one model into another one, as it can lead to inconsistencies.
You can add a ForeignKey in View to your Course model and then when you do f"{view.course.name} {view.course.department.name}" you already have your string:
class View(models.Model):
course = models.ForeignKey(Course, on_delete=models.CASCADE)
def name(self):
return f"{self.course.name} {self.course.department.name}"
Notes:
Don't call your foreign key Department_id because it's not referring to the id but to the object itself in the Django ORM: department = models.ForeignKey(Department, on_delete=models.CASCADE). As you can see, this makes reading the code much simpler: self.course.Department_id is a Department object not an integer, so self.course.department makes more sense.
Don't prefix your field names with the class, it just makes the code so much less readable: Do you prefer department.name or department.Department_name?
The View model is still a mystery to me, as you can search without it. You can search for example for courses with a matching department name like this:
Course.objects.filter(department__name__icontains="maths")
which will return all courses with "maths" in their department name.
Remove all the ids from your models, they are created automatically by Django anyway (and called id). Again, department.id is much easier to read than department.Department_id. Also in your code, you have to generate the ids yourself since you don't set them to auto-populate.

Django: pull list of records by state using zip code

I have a Django app that has a series of zip code tagged posts. I'd like to create a page that shows all posts by state but am not sure how to go about it. I do have a ZipCode table, but my Post.zipcode field is not related to it (mostly because it is user entered, and allows zips that are not in the DB or from outside the US).
My relevant models:
class Post(models.Model):
body = models.TextField()
zipcode = models.CharField(max_length=5)
class ZipCode(models.Model):
zipcode = models.CharField(max_length=5)
city = models.CharField(max_length=64)
statecode = models.CharField(max_length=2)
statename = models.CharField(max_length=32)
latitude = models.FloatField()
longitude = models.FloatField()
In my Django view I'd love to take the "state" parameter that is passed in from my url pattern and do something like this:
def posts_by_state(request, state):
posts = Post.objects.filter(zipcode__statecode=state)
...
Unfortunately, my Post.zipcode field is not a foreign key to ZipCode so I get this error if I try:
FieldError at /post/state/VT/
Join on field 'zipcode' not permitted.
Anyone have a hint as to how I should construct a queryset that pulls all posts together for a requested state? Thank you in advance.
I'd suggest updating Post.zipcode to be a ForeignKey to ZipCode. If you can't you could do the lookup like this:
zipcodes = [zip_code.zipcode for zip_code in ZipCode.objects.filter(statecode=state)]
posts = Post.objects.filter(zipcode__in=zipcodes)
On a side note, ZipCode doesn't seem like the right name for that model. Perhaps Location would be better.
Fairly easy solution in the end. What I did was add a new foreign key field to Post called location so Post now looks like this:
class Post(models.Model):
body = models.TextField()
zipcode = models.CharField(max_length=5)
location = models.ForeignKey(ZipCode, null=True, blank=True, default=None)
When I create new Posts, I check to see if the inputted zip string matches a record in the ZipCode database, and if it does I create the location FK. This then allows me to do this in my view:
def posts_by_state(request, state):
posts = Post.objects.filter(location__statecode=state)
...
Thank you Seth and sdolan for your help!