Django : Foreign Key to a choice field model - django

I am relatively new to Python / Django.
I am trying to create a relationship between food items and the category (name)
they belong:
class Category(models.Model):
options=(
('vegetable','vegetable'),
('fruit','fruit'),
('carbs','carbs'),
('fish','fish'),
('meat', 'meat'),
('sweet', 'sweet'),
('dairy', 'dairy'),
)
name=models.CharField(max_length=10,choices=options,unique=True)
def __str__(self):
return self.name
class Fooditem(models.Model):
name = models.CharField(max_length=50)
category = models.ForeignKey(Category,on_delete=models.CASCADE)
The code above throws error when running migrate:
ValueError: invalid literal for int() with base 10: 'vegetable'
I created some items in the database, is it the reason?
What is the best way to solve this problem?
Thank you,
D

It would fix it, but you may have some unexpected results.
category = models.ForeignKey(Category,on_delete=models.CASCADE)
will be an id pointing to the other model.
You can make name the primary key by changing it to the following:
name=models.CharField(max_length=10,choices=options,unique=True,primary_key=True)
Also reviewing https://docs.djangoproject.com/en/3.1/ref/models/fields/#choices more closely may help you with your options down the road. It will programming easier to define as costants and follow thier syntaxing

I would realy recommend u to do it this way. if there is something that is unclear just let me know
class Category(models.TextChoices):
vegetable='vegetable'
fruit='fruit'
carbs='carbs'
fish='fish'
meat='meat'
sweet='sweet'
dairy='dairy'
class Fooditem(models.Model):
name = models.CharField(max_length=50)
category = models.CharField(max_length=20, choices=Category.choices)```

Related

relationship choices¶widget showing text

I would like to display choices of Test_Baustoff->art Model in my Test_Objekt Model Form.
Right now i am trying to solve it with a Widget...
Models:
class Test_Objekt(models.Model):
baustoffid = models.ForeignKey(Test_Baustoff, on_delete=models.CASCADE)
bezeichnung = models.CharField(max_length=100, default='', blank=True)
class Test_Baustoff(models.Model):
art = models.CharField(max_length=100)
wert = models.IntegerField(default='0')
Forms: (found this code in the django docs... don't know if i am using it in the right way??)
class BaustoffidSelect(forms.Select):
def create_option(self, name, value, label, selected, index, subindex=None, attrs=None):
option = super().create_option(name, value, label, selected, index, subindex, attrs)
if value:
option['attrs']['data-art'] = value.instance.art
return option
class ObjektForm(forms.ModelForm):
class Meta:
model = Test_Objekt
fields = ['bezeichnung', 'baustoffid', 'bauweiseid', 'dickeaussenwand', 'dickedaemmung', 'fensterqualitaet']
labels = {'bezeichnung': 'Objekt-Bez'}
widgets = {'baustoffid': BaustoffidSelect}
html template:
<table class="table table-bordered table-light">
{{objekt_form.as_table}}
</table>
For the moment, I don't find a way to solve my problem. I looked some tutorials or StackOverflow questions but nothing up to now.
Do you have any idea about this handling?
Try to add a list of tuples with model object instances:
Baustoff_choices = []
for i in Test_Baustoff.objects.all():
Baustoff_choices.append(
(i.id,i.art)
) #second element is what is this what will be displayed in template
Then in your forms.ModelForm:
baustoffid = forms.ChoiceField(choices=Baustoff_choices)
Oh Thanks a lot. This works fine with the id!
I was already trying this approach but made the mistake that i wanted to implement this into my models... I did not know that i can also add a extra field to my forms.modelform
what i was asking myself.
is it also possible to not save the id but a foreykn key object
like in models:
#baustoffid = models.ForeignKey(Test_Baustoff, on_delete=models.CASCADE)
and in forms:
for i in Test_Baustoff.objects.all():
Baustoff_choices.append(
(**i**, i.art) # or somithing like **i.instance**
)
but i get:
Cannot assign "'Test_Bauweise object (2)'": "Test_Objekt.bauweiseid" must be a "Test_Bauweise" instance.
kind regards!
solved.
missed to add this function in my models.py
so it got displeyed like "Object(1)"...
def __str__(self): return self.art

Lookup that spans across 3 tables in Django

I want to do a lookup that spans three model classes. I want to find all the PartListings that match the Specific_part in the ListItem. Say Specific part = Radio, there could be several Radios in the PartListings and I want to return the PartListing id of all of them so that I can get other attributes like quantity.
I have these models:
class SpecificPart(BaseModel):
class PartListing(BaseModel):
specific_part = models.ForeignKey(
SpecificPart, on_delete=models.CASCADE, blank=True, null=True,
related_name="part_listing")
class ListItem(BaseModel):
specific_part = models.ForeignKey(SpecificPart, on_delete=models.CASCADE,
related_name="listitem")
I'm trying to put the lookup under the ListItem class like this:
def item_match(self):
part = self.specific_part
return PartListings.filter(specific_part__specific_part=part)
I tried to set it up as a Lookup that spans relationshipsbut am getting an error that PartListing is not defined. I also suspect that I'm referencing the foreign keys incorrectly. I'm also ok with redefining the models if a One to One would be better.
I am a Django newbie so thanks so much for your help!
You can try like this using reverse relation between SpecificPart and PartListing models:
class ListItem(BaseModel):
...
def item_match(self):
return self.specific_part.part_listing.all()

UNIQUE constraint failed

I am going through Tango with Django and I can't solve this excercise.
I get django.db.utils.IntegrityError: UNIQUE constraint failed: rango_category.name error. This is after I try to implement views attribute to Category object. This is a excerpt from my database population script. I though maybe that I should makemigrations and then migrate to update models for DB. However, this didn't help.
cats = {
'Python' : {'pages': python_pages, 'views':128},
'Django': {'pages': django_pages, 'views':128},
"Other Frameworks": {'pages':other_pages, 'views':128},
}
for cat, cat_data in cats.items():
c = add_cat(cat, cat_data['views'])
for p in cat_data["pages"]:
add_page(c, p['title'], p['url'])
for c in Category.objects.all():
for p in Page.objects.filter(category=c):
print("- {0} - {1}".format(str(c), str(p)))
def add_cat(name, views):
c = Category.objects.get_or_create(name=name, views=views)[0]
c.views=views
c.save()
return c
Adding Category model:
class Category(models.Model):
name = models.CharField(max_length=128, unique=True)
views = models.IntegerField(default=0)
class Meta:
verbose_name_plural = 'Categories'
def __str__(self):
return self.name
You got the error because unique = True in name = models.CharField(max_length=128, unique=True) means that Django will raise constraint errror UNIQUE constraint failed in case you trying to save a new instance with the same name value; A violation of the unique constraint
get_or_create doesn't work because views=views that may be different even though name equals name
When you create or get your instance, you can do so with only the name field
def add_cat(name, views):
c = Category.objects.get_or_create(name=name, views=views)[0]
c.views=views
c.save()
return c
As mentioned in comments, the error is caused by violation of the unique constraint on Category.name. The reason that this is happening might not be completely obvious, however. When you call Category.objects.get_or_create, you are asking django to look for an object with both the given name and the given number of views. If one is not found, then django tries to create one, which violates the unique constraint if there is already a Category of the same name, but with a different number of views. Depending on desired behavior, you can fix by either:
remove unique constraint altogether
change unique constraint to a unique_together constraint
change the model reference to first get or create by name, then set (or modify) the views attribute (don't forget to save)

Django sort objects with foreign key case insensitive

I'm trying to sort a query according to a foreign key field case insensitive.
My models are:
class Post(models.Model):
title = models.CharField(max_length = 80)
author = models.ForeignKey(User, default = User)
trade_option = models.CharField(max_length= 10)
class Book(models.Model):
book_title = models.CharField(max_length = 60)
post = models.ForeignKey(Post)
I want to sort my post objects according to the book_title field case insensitive.
I know if I want to sort case insensitive with the fields in Class Post I can just do:
posts = Post.objects.filter(trade_option= 'buy').extra(select =
{'lower_name' : 'lower( title )'}).order_by('lower_name')
However, when I try the same technique with sorting with foreign key book with book_title:
posts = Post.objects.filter(trade_option= 'buy').extra(select =
{'lower_name' : 'lower( book__book_title )'}).order_by('lower_name')
I get the error "no such column: book__boot_title"
I would like to know where I'm doing this wrong. Thanks
I had the same problem. Here's what you should do:
from django.db.models.functions import Lower
posts = Post.objects.filter(trade_option= 'buy').order_by(Lower('title'))
A related lookup like book__book_title only works in the context of Django. Anything in extra is not processed by Django, so these lookups won't work. You have to explicitly fetch the result from the related table.
Please note that the ordering you're trying to define is most likely not what you expect it to be: a Post object can have multiple related Book objects. Ordering by book__book_title (manually or through the orm) can give you either an indeterminate ordering or duplicate rows, depending on the exact details of your query. You need to define which related book_title the posts should be ordered by.

Django model aggregation

I have a simple hierarchic model whit a Person and RunningScore as child.
this model store data about running score of many user, simplified something like:
class Person(models.Model):
firstName = models.CharField(max_length=200)
lastName = models.CharField(max_length=200)
class RunningScore(models.Model):
person = models.ForeignKey('Person', related_name="scores")
time = models.DecimalField(max_digits=6, decimal_places=2)
If I get a single Person it cames with all RunningScores associated to it, and this is standard behavior. My question is really simple: if I'd like to get a Person with only a RunningScore child (suppose the better result, aka min(time) ) how can I do?
I read the official Django documentation but have not found a
solution.
I am not 100% sure if I get what you mean, but maybe this will help:
from django.db.models import Min
Person.objects.annotate(min_running_time=Min('time'))
The queryset will fetch Person objects with min_running_time additional attribute.
You can also add a filter:
Person.objects.annotate(min_running_time=Min('time')).filter(firstName__startswith='foo')
Accessing the first object's min_running_time attribute:
first_person = Person.objects.annotate(min_running_score=Min('time'))[0]
print first_person.min_running_time
EDIT:
You can define a method or a property such as the following one to get the related object:
class Person(models.Model):
...
#property
def best_runner(self):
try:
return self.runningscore_set.order_by('time')[0]
except IndexError:
return None
If you want one RunningScore for only one Person you could use odering and limit your queryset to 1 object.
Something like this:
Person.runningscore_set.order_by('-time')[0]
Here is the doc on limiting querysets:
https://docs.djangoproject.com/en/1.3/topics/db/queries/#limiting-querysets