Django query certain attribute via foreign key - python

I want to query certain data from a foreign key class.
Is there a way to make it directly in the query?
I want to do the following query:
plant_list = Plant.objects.filter(plant_product = ikey).values('plant_number', 'plant_name', 'plant_city', 'plant_country', 'plant_product')
My Result is:
{'plant_number': '0001', 'plant_name': 'HoP1', 'plant_city': 3, 'plant_country': 1, 'plant_product': 1}
Now, for example at plant_city. I donĀ“t want to have the ID I want to have the attribute city_name of the model City, which is the Foreign Key.
So here ist the result I want:
{'plant_number': '0001', 'plant_name': 'HoP1', 'plant_city': 'Homburg', 'plant_country': 'Germany', 'plant_product': 1}
Is there a elegant way?
That would be a very graet help!
Here are my Models:
class City(models.Model):
city_name = models.CharField(max_length=100, unique=True)
class Meta:
ordering = ['city_name']
# changes view in admin area
verbose_name_plural = ('Cities')
def __str__(self):
return self.city_name
class Country(models.Model):
country_name = models.CharField(max_length=100, unique=True)
class Meta:
ordering = ['country_name']
verbose_name_plural = 'Countries'
def __str__(self):
return self.country_name
class Plant(models.Model):
plant_number = models.CharField(max_length=10, unique=True)
plant_name = models.CharField(max_length=10, unique=True)
plant_city = models.ForeignKey(City, on_delete=models.CASCADE)
plant_country = models.ForeignKey(Country, on_delete=models.CASCADE)
plant_product = models.ManyToManyField(TPZProductCluster1)
updated_at = models.DateField(auto_now=True)
class Meta:
ordering = ['plant_number']
def plant_produces(self):
return ', '.join([p.pc1_product for p in self.plant_product.all()])
def __str__(self):
return self.plant_name + " (" + str(self.plant_number) +")"
Thank you in advance!!!

If you want to include the name of the city, you can work with an F object:
from django.db.models import F
plant_list = Plant.objects.filter(
plant_product=ikey
).values(
'plant_number',
'plant_name',
'plant_country',
'plant_product'
city=F('plant_city__city_name')
)
That being said, usually using .values() is not a good idea. If you want to serialize data to a JSON blob for example, it is better to work with serializers, for example with the Django REST framework.

With help of Willem I found the answer.
Here my Query:
plant_list = Plant.objects.filter(plant_product__pc1_product=ikey).values(
'plant_number',
'plant_name',
'plant_city__city_name',
'plant_country__country_name',
'plant_product__pc1_product'
)
Here my result:
{'plant_number': '0001', 'plant_name': 'HoP1', 'plant_city__city_name': 'Homburg', 'plant_country__country_name': 'Germany', 'plant_product__pc1_product': 'CRI-MV'}
Thanks Willem that was a great help!!!!

Related

Max occurrences of a foreign key inside query

I'm trying to get an item with the most occurrences of a foreign key (votes), inside of a queryset (Questions, Choices).
I.E. I need to get the most popular vote to set the 'winner' attribute in the JsonResponse.
Any help on how I can figure this out?
Here is my view.
allchoices = [{
'id':i.id,
'question':i.question.id,
'choice_text':i.choice_text,
'votes':i.voter_set.all().count()
} for i in poll_choices]
return JsonResponse({
"votes":choice.voter_set.all().count(),
'winner':True,
'success':True,
'allchoices':allchoices
},safe=False,)
These are my models:
class Voter(models.Model):
# Authentication of anonymous users
choice = models.ForeignKey('PollChoice', on_delete=models.CASCADE)
question = models.ForeignKey('PollQuestion', on_delete=models.CASCADE)
class PollChoice(models.Model):
question = models.ForeignKey('PollQuestion', on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200)
def __str__(self):
return self.choice_text
class PollQuestion(models.Model):
question = models.CharField(max_length=200)
created = models.DateTimeField(auto_now_add=True)
creator = models.ForeignKey('poll_creator',/\
on_delete=models.CASCADE, null=False, blank=False)
uuid = ShortUUIDField(length=8, max_length=12)
def poll_choices(self):
return self.pollchoice_set.all().annotate(voters=/\
models.Count(models.F('voter'))).order_by('-voters')
def choices(self):
return self.pollchoice_set.all()
def __str__(self):
return f'{self.question}'
You can order by the number of related Voters, with:
from django.db.models import Count
poll_choice = PollChoice.objects.alias(
num_voters=Count('voter')
).latest('num_voters')
this will retrieve the PollChoice with the largest amount of related Voters. You can filter the PollChoice further, for example to only consider PollChoices for a certain PollQuestion with:
from django.db.models import Count
poll_choice = PollChoice.objects.filter(question=my_question).alias(
num_voters=Count('voter')
).latest('num_voters')

Django Models Select a car model based on Car Make

Somewhat new to Django and I'm trying to create a car listing site. I've already ran into problems with the models. I can't seem figure out how I can create a model where if you select a particular make (e.g. Dodge) then you can select a model related to that make (e.g. Charger, Challenger, Viper etc.) or if you selected McLaren you could select from the 720s, 765lt, Senna, P1 etc.
models.py
class Make(models.Model):
make = models.CharField('Make', max_length=150)
class Meta:
ordering = ['make']
unique_together = ["make"]
verbose_name_plural = "Manufacturers"
def __str__(self):
return self.make
class CarModel(models.Model):
year = models.IntegerField(default=datetime.datetime.today().year)
make = models.ForeignKey(Make, on_delete=models.CASCADE)
model = models.CharField('Model', max_length=150)
trim = models.CharField('Trim', max_length=150, help_text='Trim level')
class Meta:
ordering = ['make', 'model', 'trim', 'year']
unique_together = ("year", "make", "model", "trim")
verbose_name_plural = "Models"
def __str__(self):
return f' {self.year} {self.make} {self.model} {self.trim}'
class CarListing(models.Model):
content = models.FileField("Media")
make = models.ForeignKey(Make, on_delete=models.CASCADE)
make_model = models.ForeignKey(CarModel, on_delete=models.CASCADE)
class Meta:
ordering = ['make_model']
verbose_name_plural = "Car Listings"
def __str__(self):
return f' {self.make_model.year} {self.make_model.make}
{self.make_model.model}
{self.make_model.trim} '
Use related_name for backwards compatibility.
class CarModel(models.Model):
year = models.IntegerField(default=datetime.datetime.today().year)
make = models.ForeignKey(Make, on_delete=models.CASCADE, related_name="models") # Note the related name here
model = models.CharField('Model', max_length=150)
trim = models.CharField('Trim', max_length=150, help_text='Trim level')
Then when you have a related name, you can easily access it by calling models on an instance
make = Make.objects.get(make="Dodge")
print(make.models) # Viper, Charger, Challenger, etc.
Note: make = Make.objects.get(make="Dodge") this will fire you an error if there are multiple records with the same query.
So you have to do something like this:
make = Make.objects.filter(make="Dodge") # return list of records`

Django ForeignKey accept two models

I'm working on this big project with Django and I have to update the database. I have to add another table which will replace another later.
So I want to add in a model the possibility to have a field where I can have either the old model OR the new one.
Here is the code of the old model:
class Harvests(models.Model):
ident_culture = models.IntegerField(primary_key=True)
intitule_culture = models.CharField(max_length=50, blank=True)
nom_fertiweb = models.CharField(max_length=50, blank=True, null = True)
affichage_quintaux_tonne = models.CharField(max_length=1,
choices=RENDEMENT_CHOICES, default = 'T')
type_culture = models.ForeignKey("TypeCulture", null=True)
slug = models.SlugField(null=True, blank=True)
image = models.ImageField(upload_to = 'images_doc_culture/',
null=True, blank = True)
affichage = models.BooleanField(default = True)
class Meta:
verbose_name = "Liste - Culture"
verbose_name_plural = "Liste - Cultures"
ordering = ['intitule_culture']
def __str__(self):
return self.intitule_culture
def label(self):
return self.intitule_culture or ''
#classmethod
def get_choices(cls):
choices = [('', corp.EMPTY_CHOICE_LBL)]
c_category_lbl, c_category = '', []
for item in cls.objects.all():
choices.append((item.pk, item.intitule_culture))
return choices
And there is the code od the new one I created:
class Crops(models.Model):
intitule_culture = models.CharField(max_length=75, blank=True)
affichage_quintaux_tonne = models.CharField(max_length=2,
choices=RENDEMENT_CHOICES, default = 'T')
type_culture = models.ForeignKey("TypeCulture", null=True)
ident_culture = models.IntegerField(primary_key=True)
affichage = models.BooleanField(default = True)
id_marle = models.IntegerField(null=True)
class Meta:
verbose_name = "Liste - Culture 2019"
verbose_name_plural = "Liste - Cultures 2019"
ordering = ['intitule_culture']
def __str__(self):
return self.intitule_culture
def label(self):
return self.intitule_culture or ''
#classmethod
def get_choices(cls):
choices = [('', corp.EMPTY_CHOICE_LBL)]
c_category_lbl, c_category = '', []
for item in cls.objects.all():
choices.append((item.pk, item.intitule_culture))
return choices
I want to accept both models in the field culture in this model:
class CompanyHarvest(models.Model):
company = models.ForeignKey('corp.Company', verbose_name='Exploitation',
related_name ='cultures')
culture = models.ForeignKey(Harvests, verbose_name ='Culture')
precision = models.CharField(max_length=255, blank=True)
saison_culture = models.CharField(max_length=1, choices=SAISON_CHOICES,
default = 'P')
class Meta:
verbose_name = "Expl. - Culture"
verbose_name_plural = "Expl. - Cultures"
unique_together = ('company', 'culture', 'precision', 'saison_culture')
def __str__(self):
return str(self.culture) + ' ' + self.precision + \
' ' + str(self.get_saison_culture_display() )
#property
def slug(self):
return "_".join([slugify(str(self.culture or '')),
slugify(str(self.precision or ''))]
)
I'm new to Django, can anyone help me with this please ? (^-^)
This is not possible - at least not this way. And this is not a Django limitation but a SQL one, a foreign key cannot reference either one table or another.
A possible and simple obvious solution here would be to have two foreign keys in CompanyHarvest - one for each of the old and new model -, each with blank=True et default=None, but it can quickly make a mess of all the client code (all code using CompanyHarvest).
Much better solutions would be to either only keep the existing model (adding any new field/feature to it and eventually hiding obsolete ones) or migrate all old model records to the new model (this can be combined with the naive "two foreign keys" solution so you can keep the old table and records as archives if necessary).
Also - totally unrelated but -, this:
#classmethod
def get_choices(cls):
choices = [('', corp.EMPTY_CHOICE_LBL)]
c_category_lbl, c_category = '', []
for item in cls.objects.all():
choices.append((item.pk, item.intitule_culture))
return choices
1/ should be defined on the manager (cf https://docs.djangoproject.com/en/2.1/topics/db/managers/#adding-extra-manager-methods)
2/ should be written using .values() queryset (which will save on both the db query and building full-blown instances for no good reason):
for item in cls.objects.values("pk", "intitule_culture"):
choices.append(item)
3/ and could very possibly (i'd have to see how it's used) replaced by a ModelChoiceField in the calling code.
Oh and yes: if you allow blanks for text fields, you very probably want to force the empty string as default so you don't two possible (and incompatible) cases (sql NULL and the empty string) when no value is given.

Django model instance fields with ManyToMany relationship to Dict

I'm trying to create a simple action that gets one record (with ManyToMany relationship) from the database then display the JSON serialized instance, here is how I did it for now:
the service model:
class SystemService(models.Model):
name = models.CharField(max_length=35, unique=True, null=False, blank=False)
verion = models.CharField(max_length=35, unique=True, null=False, blank=False)
def __str__(self):
return self.name
the server model:
class Server(models.Model):
name = models.CharField(max_length=35, unique=True, null=False, blank=False)
ip_address = models.GenericIPAddressField(protocol='both', unpack_ipv4=True,
null=False, blank=False, unique=True)
operating_system = models.ForeignKey(OperatingSystem, null=False, blank=False)
monitored_services = models.ManyToManyField(SystemService)
info = models.CharField(max_length=250, null=True, blank=True)
pause_monitoring = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.name
Here is how it is now using muj-gabriel answer:
def get_server(request, server_id):
try:
server_object = Server.objects.get(id=server_id)
data = {
'name': server_object.name,
'ip_address': server_object.ip_address,
'os': server_object.operating_system.name,
'info': server_object.info,
'monitoring_paused': server_object.pause_monitoring,
'created_at': server_object.created_at,
'update_at': server_object.updated_at,
'services': {service['id']: service['name'] for service
in server_object.monitored_services.values('id', 'name')}
}
return JsonResponse(data)
except Server.DoesNotExist:
return JsonResponse({'error': 'Selected object does not exits!'})
I don't think that what I did is good enough since I have to repeat the same thing each time I need to get one instance as JSON, so I would like to know if is there a pythonic and dynamic way to do it?
If you just need the values 'id' and 'name' I suggest using this:
'services': {service['id']: service['name']
for service in server_object.monitored_services.values('id', 'name')}
See django docs
Also you can move the code into a property to the Model class to reuse it elsewhere.
class Server(models.Model):
.......
#property
def data(self):
return {
'name': self.name,
'ip_address': self.ip_address,
'os': self.operating_system.name,
'info': self.info,
'monitoring_paused': self.pause_monitoring,
'created_at': self.created_at,
'update_at': self.updated_at,
'services': {service['id']: service['name'] for service in self.monitored_services.values('id', 'name')}
}
Your view function will be:
def get_server(request, server_id):
try:
server_object = Server.objects.get(id=server_id)
return JsonResponse(server_object.data)
except Server.DoesNotExist:
return JsonResponse({'error': 'Selected object does not exits!'})
After looking a bit in the Django doc I found the model_to_dict function, which basically do what I need (Model instance to Dict), but for the ManyToMany relationship it only returns a list of PKs, so I wrote my own function based on it:
def db_instance2dict(instance):
from django.db.models.fields.related import ManyToManyField
metas = instance._meta
data = {}
for f in chain(metas.concrete_fields, metas.many_to_many):
if isinstance(f, ManyToManyField):
data[str(f.name)] = {tmp_object.pk: db_instance2dict(tmp_object)
for tmp_object in f.value_from_object(instance)}
else:
data[str(f.name)] = str(getattr(instance, f.name, False))
return data
Hope it helps someone else.

django querying from 3 models

My models are :
model 1:
class source_of_enquiry(models.Model):
source_of_enquiry = models.CharField(max_length=200, null=True, blank=True)
def __unicode__(self):
return '%s' % self.source_of_enquiry
model 2:
class customers(models.Model):
cutomer_name = models.CharField(max_lentgth=200)
customer_src_n_type = models.Foreign_key(source_of_enquiry)
customer_contact = models.CharField(max_lentgth=200)
def __unicode__(self):
return '%s' % self.customer_name
model 3:
class sales_cycle(models.Model):
item_name = models.CharField(max_length=200)
customer_name = models.Foreignkey(customers)
def __unicode__(self):
return '%s' % self.item_name
how should i know how many sales had peen completed based on source of enquiry??
tried many from `select_related' and 'prefetch_selected' , but all were kaput.
First of all - python naming convention state that classes should not have underscores and prefer upper-case letters instead. So your models should be SourceEnquiry, Customer (not plural) and SaleCycle.
That being said, let's say I have a SourceEnquiry item (I'm going to pick one arbitrarily), and you want all related SaleCycle items, you do it like so:
>>> sinq = SourceEnquiry.objects.get(pk=1)
>>> SaleCycle.objects.all().filter(customer_name__customer_src_n_type=sinq)
p.s.
also, going back to the naming convention thing, it's redundant to use customer as part of a field name inside the class Customer. You alread know it's a customer object, so it's better to name it like so:
class Customer(models.Model):
name = models.CharField(max_lentgth=200)
src_n_type = models.Foreign_key(source_of_enquiry)
contact = models.CharField(max_lentgth=200)
You other fields can also be cleaner:
class SourceEnquiry(models.Model):
value = models.CharField(max_length=200, null=True, blank=True)
class SaleCycle(models.Model):
item = models.CharField(max_length=200)
customer = models.Foreignkey(Customer)

Categories