Get object fields from ForeignKey in RadioSelect - django

I have a model with a ForeignKey field and a form to create and update the objects.
I choose the ForeignKeyField with a RadioSelect widget and loops through the radio inputs in the template with {% for radio in form.foreign_key_items %}{{ radio.tag }} - {{ radio.choice_label }}{% endfor %}.
My form is:
class ItemForm(ModelForm):
class Meta:
model = Item
fields = ('foreign_key_item',)
widgets = {'foreign_key_item': RadioSelect,}
But the choice_label is not enough information for the user to select the correct object.
How is it possible to print fields from the foreign_key_item objects when I print each radio?

You can compile all the appropriate information in the view and add it to the context that is rendering the form. Alternatively, you can probably use select_related.

Related

Django DetailView - display boolean from models as checkboxes

There's a number of snippets to display booleans in Django Forms as checkboxes (i.e. specifying Checkbox as a widget). For instance (assuming a boolean field defined in the model for bar):
class FooForm(forms.ModelForm):
class Meta:
model = Foo
fields = ['bar']
widgets = {
'bar' : CheckboxInput(attrs={'class': 'required checkbox form-control'}),
}
However I also need to display a (disabled) checkbox in DetailView (client says so). But I can't figure out an elegant way to do this, since I don't have a form meta for details view...
My current thinking is something like this (bootstrap checkbox):
<div class="form-check">
<label class="form-check-label">
<input type="checkbox" {% if foo.bar %}checked{% endif %} disabled>Bar
</label>
<\div>
Any way to accomplish this in a fashion closer to the Form's widgets?
in the view get you form and set initial value
get the model object and set bars initial value
form = YourForm(initial={'bar':modelObject.bar })
and then send the form to the template and simply render
like form.bar
you can disable this with many ways
like
class FooForm(forms.ModelForm):
class Meta:
model = Foo
fields = ['bar']
widgets = {
'bar' : CheckboxInput(attrs={'class': 'required checkbox form-control','disabled':'disabled or true'}),
}
or find and use any template filter to add attribute to form field

How do I display Django ManyToMany on a template? Simple code needed

I am trying to display ManyToMany field on the template in reversed order.
Here is what I mean:
I managed to display ManyToMany field on template when ManyToMany field was a field in model used so for example:
<br/>{% for tag in post.tag.all %}{{ tag }}<br/>{% endfor %}
will display all of the tags(meaning categories) that the post belongs to based on this model:
class Post(models.Model):
tag = models.ManyToManyField(Tag,blank=True,null=True,related_name='tag')
Now I want something opposite - display authors of the post when ManyToMany field is in the Author model (Post model above stays the same):
class Person(models.Model):
post=models.ManyToManyField(Post,blank=True,null=True,related_name='post')
I am quite sure it has something to do with Related Object Reference ( https://docs.djangoproject.com/en/2.2/ref/models/relations/)
Just can not make it work.
I have tried the following on the template.
{% for post in posts %}
{% for author in post.person_set.all %}{{author}}<br/>{% endfor %}
{% endfor %}
Also, shall I do this kind of searches on the template like above or is it a better practice put this kind of searches in views...resourcewise.
Thanks for help.
You have a misunderstanding on what the related_name= parameter [Django-doc] does. Like the documentation says:
The name to use for the relation from the related object back to this one. (...)
So it is the name of the relation in reverse. In order to make your models "sound", you thus should name it like:
class Person(models.Model):
posts = models.ManyToManyField(Post, blank=True, null=True, related_name='authors')
It also makes sense here to use the plural, so posts instead of post.
In that case, you thus can render this with:
{% for post in posts %}
{% for author in post.authors.all %}{{author}}<br/>{% endfor %}
{% endfor %}
Note that if you want to render all the values for ManyToManyFields, you better use .prefetch_related(..) in the queryset to prefetch the Person,s otherwise rendering the template will result in a lot of extra queries.

In a forms of an inlineformset how can I know which are my default form fields and which are the ones added by Django?

I have a specific formset (inlineformset), and I want to make some customization regarding the default design.
I loop thru it this way:
{% for form in formset %}
<div class="ct-formset">
{% if form.errors %}<div>{{ form.errors }}</div>{% endif %}
{% for field in form %}{{ field }}{% endfor %}
{% endfor %}
For each form Django add two other fields the Foreign key field and the Delete Field.
Because I want to use the same code for multiple formsets, In the loop I don't request the field by name
I need to know in the:
{% for field in form %}{{ field }}{% endfor %}
How can I know which are my default form fields and which are the ones added by Django ?
While working with the form instance, you can't tell for sure what fields are added originally in the class or after instantiation.
Though, here you have some approaches on how to prevent those fields you don't want to be rendered in the resulting formset.
If you have access to the form's class ...
... at the moment you need to "filter" which fields where the original fields of such form you could:
>>> class MyForm(forms.Form):
>>> title = forms.CharField()
>>> class_dict = vars(MyForm)
>>> class_dict['declared_fields']
OrderedDict([('title', <django.forms.fields.CharField at 0x7f496ce067d0>)])
About *-DELETE and *-ORDER fields
When dealing with formsets, you must to have into account that not only Django can add extra fields to your forms, you also can do that.
First, we have to understand why these fields are there.
The case for the *-DELETE and *-ORDER fields are added just if you enable them using:
formset = formset_factory(..., can_delete=True, can_order=True)
Django uses BaseFormSet.add_fields in order to add that extra fields it needs to enable ordering or deletion, you could use it to add your own additional fields too.
What can you do?
You can just pass False there.
An interesting experiment ...
I perform an experiment in order to illustrate the effect overwritting this can have:
from django import forms
class MyForm(forms.Form):
title = forms.CharField()
class MyBaseFormSet(forms.BaseFormSet):
def add_fields(self, form, i):
# Avoiding FormSets using this to add additional fields
return form
# Create a form set with deletion and ordering enabled.
# pay attention to the argument: formset=MyBaseFormSet
MyFormSet = forms.formset_factory(MyForm, formset=MyBaseFormSet, can_delete=True, can_order=True)
fs = MyFormSet()
for form in fs:
print(form.as_table())
The result, no DELETE or ORDER fields added to the resulting forms.
>>> <tr><th><label for="id_form-0-title">Title:</label></th><td><input type="text" name="form-0-title" id="id_form-0-title" /></td></tr>
What about ForeignKey
That's something you can solve in the forms. If you don't want ForeignKey to be displayed, you could use forms.HiddenInput widget for such fields in your forms.
Also if you're working with ModelForms you can select the fields to use for generating the form.
Conclusion
With this info, I hope you to be able to plan how to implement your requirement of having a generic template for those formsets.
Starting points:
Pass can_delete or/and start_order as False to formset_factory.
Set forms.HiddenInput widget for ForeignKey fields in your forms.
Find a way to get the form class and use vars in order to find out the
original fields of the form.
If you're using ModelForms, use the fields or exclude meta configuration in order to state which fields are used to build the form.

Django filter related_name subset in templates

I have a foreign key and I'm using the related_name field like so:
class Pizza(models.Model):
...
restaurant = models.ForeignKey('Restaurant', related_name='pizzas_offered')
active = models.BooleanField(...)
...
Example from the view:
my_restaurant = get_object_or_404(Restaurant, pk=id)
In any template I can run something like my_restaurant.pizzas_offered.all to get all the pizzas belonging to a particular restaurant. However, I only want the pizzas that are active (active=True). Is there a way to retrieve this subset in the template, without having to pass a separate variable in the view? Please note that I always want to only show the active pizzas only so if I have to make a change in the model to make this happen, that is fine too.
NOTE: in the view I can simply pass my_restaurant.pizzas_offered.filter(active=True) but it returns an error when I use it in the template:
{% for details in my_restaurant.pizzas_offered.filter(active=True) %}
{{ details.name }}
{% endfor %}
It returns this error:
Could not parse the remainder: '(active=True)'
There are some reasons why I want to do this on template level and not in the view (main reason: I often loop through all the records in the database and not just one restaurant, so I can't just query for the one record). So my question is how to do this on template-level.
You need to create a Manager for your Pizza model and set the Meta.base_manager_name:
class PizzaManager(models.Manager):
def active(self):
return self.filter(status=True)
class Pizza(models.Model):
...
objects = PizzaManager()
class meta:
base_manager_name = 'objects'
Now you can use the method active in your template:
{% for details in my_restaurant.pizzas_offered.active %}
...
{% endfor %}
For more information you can read the documentation about Default and Base managers.

Django ForeignKey form field: display value

I might just be having a brain fart, but I seriously can't figure this out. How do I get display the values of ForeignKey fields from a bound form in a template? {{ field }} renders a widget, {{ field.value }} returns the pk, and I need the model itself.
I am writing a custom template for django-crispy-forms; which is why I only get a form field in my context, but not a model field.
You must try this: {{ form.instance.field }}