Django ManytoManyField and widgets - django

I have two models, Product and Category and ManytoMany field in Product.
The Category appear as key on ProductCreate View.
I need to customize the widget and field for Categories.
I checked in Django source Fields and Widgets but I don't see a reference(class) for ManyToMany.
To what type of Field and Widget ManyToMany relationship corresponds(I presume is Charfield as save or SelectField)? Where I can find the code ? (an example to customize field/widget in this case)

A model ManyToManyField is represented as a MultipleChoiceField and the default widget is SelectMultiple But, we can customise it.
You can find it in below references.
[1]https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#field-types
[2]https://docs.djangoproject.com/en/dev/ref/forms/widgets/#setting-arguments-for-widgets

from django import forms
from . import models
class ProductForm(forms.ModelForm):
class Meta:
model = models.Post
fields = [<fields-for-your-product-form>]
categories = forms.ModelMultipleChoiceField(
queryset=models.Category.objects.all(),
widget=forms.CheckboxSelectMultiple
)
This will render checkboxes in your form but you can substitute it with your preferred widget.
I've done something similiar where I was adding tags to blog posts. I wrote a tutorial for it- goes into more detail: https://ctrlzblog.com/how-to-add-tags-to-your-blog-a-django-manytomanyfield-example/

Related

Django - Is it possible to use other value than pk for modelform field?

I have a model form and I post the form via AJAX. One of the fields is a manytomany field and I render it as checkboxes. By default django uses PKs of the models in the queryset that is passed in. Is there a way to use another field of the model as value instead of PK values. (I have PK as integer - for a reason. I also have a UUID field which is not PK. I want to use that one for values.)
In your model form define your uuid field explicitly and pass extra to_field_name="uuid_field" argument so when you render your form, options of your html select input will be rendered as:
<option value="obj.uuid_field">Model Instance</option>
Example model form:
class YourModelForm(forms.ModelForm):
uuid_field = forms.ModelChoicefield(
queryset=Model.objects.all(),
to_field_name="uuid_field"
)
class Meta:
model = YourModel
fields = '__all__'
For more information here is documentation.

How can I change the display name for Django admin filter_horizontal

Assume I have a product object and category object with many-to-many relation. I want to use filter_horizontal to edit them in admin page. How can I change the text display in the form by category object 1 to the text of category except override __str__ in the model? Because I don't want to this change globally.
I try to use customer label_from_instance from ModelMultipleChoiceField class, but this will break filter_horizontal.
class ProductAdmin(admin.ModelAdmin):
filter_horizontal = ['categories']

Django: Search many to many field while creating object

I've got a use case where I have multiple Ingredient that can be linked to a Recipe through the Django admin. Now I have around a hundred ingredients which makes it very difficult to select the ingredients in the following UI.
Is there a way to add a search field or something similar to the django admin for easier selection?
You have few choices.
1. filter_horizontal
With filter_horizontal, you can use horizontal m2m ui in admin. I prefer this way using m2m in admin.
class YourAdmin(admin.ModelAdmin):
filter_horizontal = ('m2m_field',)
...
And the result will be...
2. raw_id_fields docs
You can use raw_id_fields for using pop-up modal with your m2m fields.
It's bit useful when you have lots of m2m field. Also, it's easy to filter which m2m obj to add.
class YourAdmin(admin.ModelAdmin):
raw_id_fiedls = ('m2m_field',)
...
I suppose you want to filter over ingredients and select it one by one on admin UI
You can use django forms builtin CheckboxSelectMultiple
widget in place of SelectMultiple to make selection easy
from django import forms
from django.contrib import admin
class RecipeForm(forms.ModelForm):
class Meta(object):
model = Recipe
widgets = {
'Ingredient': forms.CheckboxSelectMultiple,
}
class RecipeAdmin(admin.ModelAdmin):
form = RecipeForm
admin.site.register(Recipe, RecipeAdmin)
Alternatively, you can use django-better-filter-widget
package if you want a search input on choices, Refer Github repo for
installation
It is a custom widget, created by overriding SelectMultiple widget of
django forms
from django import forms
from django.contrib import admin
from better_filter_widget import BetterFilterWidget
class RecipeForm(forms.ModelForm):
class Meta(object):
model = Recipe
widgets = {
'Ingredient': BetterFilterWidget(),
}
class RecipeAdmin(admin.ModelAdmin):
form = RecipeForm
admin.site.register(Recipe, RecipeAdmin)

Replace foreign key dropdown with textbox with add/edit icon - Django

Related to foreign key fields in the Django Admin, the default display
element is a drop down list box containing all of the foreign key
items from the related model. I my app it will contain thousands of items and I am looking to change the admin interface and have it use a text box instead of the populated drop down.
Looking for textbox with add/edit icon next to it, so that we dont get populated values, we just directly add or edit.
Is there any way around to achieve it.
What you are looking for is raw_id_fields
class ArticleAdmin(admin.ModelAdmin):
raw_id_fields = ("newspaper", )
By default, Django’s admin uses a select-box interface (<select>)
for fields that are ForeignKey. Sometimes you don’t want to incur
the overhead of having to select all the related instances to display
in the drop-down.
raw_id_fields is a list of fields you would like to change into an
Input widget for either a ForeignKey or ManyToManyField:
NOTE: The raw_id_fields Input widget should contain a primary key if the field is a ForeignKey or a comma separated list of values if the field is a ManyToManyField. The raw_id_fields widget shows a magnifying glass button next to the field which allows users to search for and select a value:
You can read the docs here
You can try use custom Form and realize custom Widget on this form field. (I use for this desicion 3rd party library django_select2)
from django import forms
from django_select2.forms import ModelSelect2Widget
class KeyWidget(ModelSelect2Widget):
model = ModelToKey
search_fields = ['field__icontains']
def label_from_instance(self, obj):
return u'{}'.format(obj.field)
class CustomForm(forms.ModelForm):
class Meta:
model = ModelWithKey
fields = ('foreign_key_field')
widgets = {
'foreign_key_field': KeyWidget(attrs={'style': 'width:550px'}),
}
In also you can overload Form __init__ for customize queryset of objects for choosing in this field.

admin site doesn't show object fields

I have a model with a PointField from django.contrib.gis.db.models . This somehow doesn't let the admin site show the objects with a nice table of fields and values. Instead it displays one field named after the model name. The values are just a bunch of ' object'. With the name of the model. If I click the object I can edit it fine. It would be nice however to be able to filter and see the field values at the admin/ page itself.
Since PointField does not have a __unicode__ attribute, for the proper name to show up, you can register a new admin model object.
Now, in the admin's list_display,
class PointFieldAdmin(admin.ModelAdmin):
list_display = ('name', 'field_x', 'field_y', ...)
admin.register(PointField, PointFieldAdmin)
More on admin models registering here