relationship choices¶widget showing text - django

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

Related

Model Admin Error: Cannot Exclude Field because it is a Foreign Key to the parent model

My goal is to be able to select a location and Input part numbers without seeing this quote field. I dont even completely understand what this select box is looking for. I have Quote objects saved and yet these are not coming up as selectable options. Not that I want them to, Im just saying. My thinking regarding the seelctable options is that this would be auto-populated? You can probably tell my confusion even in my explanation. Ultimately, I dont want to see a select box at all as Im not really interested in whatever this pointing to, but just for kicks would like to know what it is trying to point to.
quote/Models.py
class Quote(models.Model):
QUOTE_ENVIRONMENTS = (
('testing', 'Test'),
('production', 'Production')
)
SALES_SOURCE=((1, 'Marketplace'),
(2, 'Webstore'),
(3, 'Physical Store'),
(4, 'Phone')
)
environment = models.CharField(max_length=20, choices=QUOTE_ENVIRONMENTS, default="testing")
sales_source = models.IntegerField(choices=SALES_SOURCE, null=True)
order_notes = models.TextField(blank=True)
locations = models.ManyToManyField('products.ProductSelection')
products/models.py
class Product(models.Model):
pass
class Warehouse(models.Model):
pass
class ProductSelection(models.Model):
location = models.ForeignKey('Warehouse', on_delete = models.CASCADE)
product = models.ManyToManyField('Product')
Admin.py
class ProductOrderForm(forms.ModelForm):
locations = forms.ModelChoiceField(queryset= Warehouse.objects.all())
part_number = forms.IntegerField()
def clean_product_id(self):
cd = self.cleaned_data
logger.info(cd)
value = cd['part_number']
if value not in Products.objects.list_part_numbers():
raise forms.ValidationError("Not a valid partnumber")
class ProductSelectionTabularInline(admin.TabularInline):
form = ProductOrderForm
model = Quote.locations.through
class QuoteAdmin(admin.ModelAdmin):
list_display=['id', 'environment', 'order_notes','sales_source']
list_editable = ['environment', 'sales_source', 'order_notes']
inlines = [ProductSelectionTabularInline]
exclude=['quote']
Error when using exclude attr.
ERRORS:
<class 'orders.admin.ProductSelectionTabularInline'>: (admin.E201) Cannot exclude the field 'quote', because it is the foreign key to the parent model 'orders.Quote'.
I dont want the left most box. Thanks for your help
I figure out that the field to the left is the ProductSelection instance. I confused myself by adding the other 2 form widgets. So this does not allow me to do what I want which is to edit the parts to the locations and add it to the form for creating a quote.

Django : Foreign Key to a choice field model

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)```

Recursive relationship in Django: get all the interrelated records from each record

I have this model for a tune:
class MsTune(models.Model):
name = models.CharField(max_length=255)
ms = models.ForeignKey(Manuscript, on_delete=models.CASCADE, related_name="mstunes")
concordances = models.ManyToManyField("self", blank=True)
I have many manuscripts (ms), and the same tune might appear in more than one manuscript. What I want to do, with that concordances field, is link all the similar tunes. This works, in part. The Django administration back-end allows me to select many tunes to be linked to the current one I'm editing:
The problem is that I'd like to see ALL the tunes I've linked in EACH of the tune. For instance, in the example above, Mary Scott... should list all three tunes (Courante, Almain, Hopetounsetc...); Courante should list the other three (Mary Scott, Almain, Hopetoun's). Right now they only show the one from which editing page I've added them. What am I missing? Do I have to create a new concordances model?
PS
Specifying symmetrical=True doesn't help.
Better explanation of what I want to achieve
If I have these tunes:
tune_1
tune_1a
tune_1b
tune_2
Right now I'm getting this:
tune_1:
concordances.all: tune_1a, tune_1b
tune_1a:
concordances.all: tune_1
tune_1b:
concordances.all: tune_1
I want this:
tune_1:
concordances.all: tune_1a, tune_1b
tune_1a:
concordances.all: tune_1, tune_1b
tune_1b:
concordances.all: tune_1, tune_1a
Partial solution with intermediary model
Let's say I use an intermediary model:
class MsTune(models.Model):
related = models.ManyToManyField('MsTune', through='Concordance', blank=True)
[...]
class Concordance(models.Model):
tune = models.ManyToManyField(MsTune)
title = models.CharField(max_length=64)
def __str__(self):
return self.title
How do I then access the list of interrelated tunes in a template where I'm displaying the details of a specific tune?
{% for concordance in tune.concordance_set.all %} {{ concordance__mstune_id }}{% endfor %}
Doesn't output anything; and [...]{{ concordance }}[...] outputs the name of the concordance, not the title of each tune.
Accessing {{ tune.concordances }} gives me a 'ManyToManyField' object has no attribute '_m2m_reverse_name_cache' error.
I solved by creating a different model:
class Concordance(models.Model):
name = models.CharField(max_length=64)
tunes = models.ManyToManyField(MsTune, related_name="concordances")
def __str__(self):
return self.name

Django MPTT tree as model filter in admin

I have a model linked to a related model that is a Django MPTT tree model, I would like to be able to filter the first model using the Django MPTT tree in the admin console.
class Tenders(models.Model):
...
sector=models.ForeignKey(Sector, to_field='sectorId', null=True, blank=True,on_delete=models.CASCADE)
...
class Sector(MPTTModel):
name = models.CharField(max_length = 255)
parent = TreeForeignKey('self', on_delete=models.CASCADE, null=True, blank=True,related_name='children')
sectorId = models.IntegerField(default=0,null=True,unique=True)
In the Django admin I would like to set up the filters for the Tenders model such that the Django-MPTT tree is the filter.
I have tried using the following:
class adminTenders(admin.ModelAdmin):
def linkTo(self,obj):
return mark_safe("""<a href='{}' target="_blank" >Tender Link</a>""".format(obj.tenderLink))
linkTo.short_description=''
list_display=(
'title',
'linkTo',
'sector',
'region',
'repository',
'id',
)
list_filter=(
('sector', TreeRelatedFieldListFilter),
)
admin.site.register(Tenders,adminTenders)
However I get the following error when trying to run this and I cant figure it out:
File "py36/lib/python3.6/site-packages/mptt/admin.py", line 314, in field_choices
mptt_level_indent * levels_dict[pk])
KeyError: 0
Any help would be greatly appreciated.
Edit 1: I think I have isolated the issue to the fact that my foreign key in Tenders to Sectors uses a to_field='sectorId instead of the default to link to the pk column. This had to be done for backwards compatibility to an old database scheme that I am stuck with.
So it turns out this is a bug in the django-mptt code for the field_choices function in the TreeRelatedFieldListFilter class.
To fix it I had to subclass and over ride that function to use the to_field that I had defined.
Here is the custom code:
class TreeRelatedForSectors(TreeRelatedFieldListFilter):
# Modified from django-mptt code to fix to_field problem
def field_choices(self, field, request, model_admin):
mptt_level_indent = getattr(model_admin, 'mptt_level_indent', self.mptt_level_indent)
language_bidi = get_language_bidi()
initial_choices = field.get_choices(include_blank=False)
pks = [pk for pk, val in initial_choices]
models = field.related_model._default_manager.filter(sectorId__in=pks)
levels_dict = {model.sectorId: getattr(model, model._mptt_meta.level_attr) for model in models}
choices = []
for pk, val in initial_choices:
padding_style = ' style="padding-%s:%spx"' % (
'right' if language_bidi else 'left',
mptt_level_indent * levels_dict[pk])
choices.append((pk, val, mark_safe(padding_style)))
return choices

Django - show in template related class count filtered by parameter

I will give my models first and then write description.
class Entry(models.Model):
entry_text = models.TextField()
class Category(models.Model):
user = models.ForeignKey(User)
category_text = models.CharField(max_length=200)
entries = models.ManyToManyField(Entry, through='CategoryEntry')
class CategoryEntry(models.Model):
category = models.ForeignKey(Category)
entry = models.ForeignKey(Entry)
viewed = models.BooleanField(default=False)
So I have Entry model and Category model, and I have created intermediate model CategoryEntry as descriebed here https://docs.djangoproject.com/en/1.7/topics/db/models/#extra-fields-on-many-to-many-relationships because I need one extra field "viewed" (marked as True when user for the first time opens specific Entry link).
So I have created generic.ListView view, where I show all these categories that user has created for himself. What I want, is to show next to every category name, how many entries there are and how many entries he hasn't viewed yet.
Like:
Category Total Not_viewed
AAA 126 5
BBB 17 15
I have managed to show total entries in template by
{% for category in categories %}
{{ category.text }}
{{ category.entries.count }}
{% endfor %}
In my view I have get_queryset like
def get_queryset(self):
categories = Category.objects.filter(user=self.request.user.id)[:]
return categories
As I understand, then the best way would somehow add this extra info about every categories entries viewed count in get_queryset. I have searched around but didn't found anything what works. Have tried some things with select_related, prefetch_related, annotate but don't get whats the right way to do this.
Know that it's not right, but tried something like that and some other things.
categories = Category.objects.filter(user=self.request.user.id).select_related('categoryentry').filter(categoryentry__viewed=False).count()
categories = Category.objects.filter(user=self.request.user.id).annotate(not_viewed_count=Count('categoryentry')).filter(not_viewed_count__viewed=False)
Hope you get my idea what I wan't to achieve.
In your CategoryEntry model, use related_name in the category field like so:
category = models.ForeignKey(Category, related_name="related_entry_categories")
Now you can use this related name when querying the Category model. For example:
from itertools import chain
categories_not_viewed = Category.objects.filter(user=self.request.user.id, related_entry_categories__viewed=False).annotate(num_not_viewed=Count('related_en‌​try_categories'))
categories_viewed = Category.objects.filter(user=self.request.user.id, related_entry_categories__viewed=True).extra(select={'num_not_viewed': 0})
categories = chain(list(categories_not_viewed), list(categories_viewed))
At end I came up with this solution:
categories = Category.objects.filter(user=self.request.user.id).extra(select = {
"num_not_viewed" : """
SELECT COUNT(*)
FROM app_categoryentry
WHERE app_categoryentry.category_id = app_category.id
AND app_categoryentry.viewed = %d """ % 0,
})
Based on the solution from this resource http://timmyomahony.com/blog/filtering-annotations-django/
If anyone have other solution how the get the same result with only Django ORM, I would like to know.