Django how to render ModelForm field as Multiple choice - django

I have a model form like this:
from django.forms import widgets
class AdvancedSearchForm(forms.ModelForm):
class Meta:
model= UserProfile
fields = ( 'name', 'location', 'options')
Where 'options' is a list of tuples and is automatically rendered in template as drop down menu. However I want users to be able to choose multiple options in the search form.
I know I need to add a widget to the form class as I looked at the docs and it seems to me that it needs to be something like this to the class:
widgets = {
'options': ModelChoiceField(**kwargs)
}
However I get this error
name 'MultipleChoiceField' is not defined
So Finally could not figure out how exactly to implement that. So appreciate your help.

ModelChoiceField is not a widget, it's a form field, but to use multiple version of it, you need to override field:
class AdvancedSearchForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(AdvancedSearchForm, self).__init__(*args, **kwargs)
self.fields['options'].empty_label = None
class Meta:
model= UserProfile
fields = ( 'name', 'location', 'options')
then to override the widget to checkboxes, use CheckboxSelectMultiple
widgets = {
'options': forms.CheckboxSelectMultiple()
}

Related

Django custom form attrs removes css class

I'm dealing with an RTL CharField so I impelemted the below in my admin.py:
class CustomPostForm(forms.ModelForm):
class Meta:
model = Post
fields = ['title', 'slug', 'lead', 'body', 'status', 'is_featured']
widgets = {'title': forms.TextInput(attrs={'dir': 'rtl'})}
#admin.register(Post)
class PostAdmin(admin.ModelAdmin):
form = CustomPostForm
This works fine except that when I look at the form in the Admin site the width of the RTL field is less than the next LTR field:
And if I remove the whole custom form, then both fields look the same:
So I inspect the code and realize that the slug field has a css class called vTextField and if I change the custom form's attrs both fields look the same:
widgets = {'title': forms.TextInput(attrs={'dir': 'rtl', 'class': 'vTextField'})}
So my question is, is it normal or intentional for Django to remove CSS class if attrs is used and my approach in putting the class back is correct, or I'm missing something here?
You are replacing attrs {'class': 'vTextField'} with {'dir': 'rtl'}. So yes, original attrs is replaced with yours. It's normal and expected.
Add the attribute inside init method:
class CustomPostForm(forms.ModelForm):
class Meta:
model = Post
fields = ['title', 'slug', 'lead', 'body', 'status', 'is_featured']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['title'].widget.attrs['dir'] = 'rtl'

set html class to TextField in django model

I want to change TextField in my model to TinyMCE RichTextEditor. I have downloaded tinymce 4.8.3 development package and customized it. Then I pasted tinymce files to staticfiles directory. As I understand it, in order for everything to work, I must assign an HTML class for the <textarea> </textarea> element on the view page. How it works on the local html file. I've tried almost all the applications that are installed through the pip(django-tinymce, django-tinymce4-lite etc..). And I still wanted to adjust this rich text editor for myself. Rich Text Editor must be appeared on admin page.
This is my models.py:
class Post(models.Model):
title = models.CharField(max_length=150)
content = models.TextField()
created = models.DateTimeField(auto_now_add=True)
tags = TaggableManager(blank=True)
class Media:
js = ('js/init-tinymce.js', 'js/tinymce/tinymce.js',)
admin.py:
class PostModelAdmin(admin.ModelAdmin):
list_display = ["title", "created"]
list_display_links = ["title"]
list_filter = ["created", "tags"]
search_fields = ["title", "content",]
class Meta:
model = Post
admin.site.register(Post, PostModelAdmin)
Please explain to me if I understood something differently. I use Django 2.0.8 and python 3.6. On the Internet, I found how to set a class like in this form, but I can not add it to my project.
class MyTextForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(MyTextForm, self).__init__(*args, **kwargs)
self.fields['content'].widget.attrs['class'] = 'tinymce'
class Meta:
model = Post
fields = ('content') # your fields here
You need to create a form :
class MyModelAdminForm(forms.ModelForm):
class Meta:
fields = '__all__'
model = Post
widgets = {
'yourfieldname': forms.Textarea(attrs={'class': 'your-class'})
}
And now add it to you model admin :
class PostModelAdmin(admin.ModelAdmin):
form = MyModelAdminForm
You're nearly there with the code you have, just a few things:
The nested Media class should be on the form or the widget, not on the model
If you always want the same CSS class in several places, you can encapsulate it all in a Widget
To override the behaviour of the form used by the admin, you have 2 main options:
set the form class attribute like in the other answer, but you have to implement the form
override the default widget for a certain model field using formfield_overrides, this way you just have to write a widget once.
Here is the widget code:
class AdminTinyMceWidget(AdminTextareaWidget):
class Media:
js = ('js/init-tinymce.js', 'js/tinymce/tinymce.js',)
def __init__(self, attrs=None):
if attrs is None:
attrs = {'class': 'your-class'}
super().__init__(attrs)
And the model admin setting formfield_overrides:
class PostModelAdmin(admin.ModelAdmin):
formfield_overrides = {
models.TextField: {'widget': AdminTinyMceWidget()},
}

Add `help_text` to Django Admin field

I'm attempting to display an image when editing a user on the admin panel, but I can't figure out how to add help text.
I'm using this Django Admin Show Image from Imagefield code, which works fine.
However the short_description attribute only names the image, and help_text doesn't seem to add an text below it.
How can I add help_text to this field like normal model fields?
EDIT:
I would like to have help_text on the image like the password field does in this screenshot:
Use a custom form if you don't want change a model:
from django import forms
class MyForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
self.fields['image'].help_text = 'My help text'
class Meta:
model = MyModel
exclude = ()
#admin.register(MyModel)
class MyModelAdmin(admin.ModelAdmin):
form = MyForm
# ...
It took me a while to figure out. If you've defined a custom field in your admin.py only and the field is not in your model. Use a custom ModelForm:
class SomeModelForm(forms.ModelForm):
# You don't need to define a custom form field or setup __init__()
class Meta:
model = SomeModel
help_texts = {'avatar': "User's avatar Image"}
exclude = ()
And in the admin.py:
class SomeModelAdmin(admin.ModelAdmin):
form = SomeModelForm
# ...
If you don't want to create a custom model form class :
class MyModelAdmin(admin.ModelAdmin):
def get_form(self, request, obj=None, change=False, **kwargs):
form = super().get_form(request, obj=obj, change=change, **kwargs)
form.base_fields["image"].help_text = "Some help text..."
return form

Django: disappearing filter_horizontal after overriding a form field

I have a many2many field in model:
models.py
pages = models.ManyToManyField(Page, verbose_name='Pages', blank=True)
And for admin interface filter_horizontal works just fine:
admin.py
filter_horizontal = ['pages',]
But when i overriding this field, using forms.Modelform (for changing queryset) - in interface it begins to show like a simple <select> field:
forms.py
class BannerAdminForm(forms.ModelForm):
pages = forms.ModelMultipleChoiceField(queryset=Page.objects.filter(publisher_is_draft=0), label='Pages')
class Meta:
model = Banners
admin.py
class BannersAdmin(admin.ModelAdmin):
form = BannerAdminForm
filter_horizontal = ['pages',]
Is there any solution for this problem? I looked for some special widgets for ModelMultipleChoiceField, but don't find anything.
This doesn't address the actual issue but is an alternative approach to setting the queryset:
class BannerAdminForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(BannerAdminForm, self).__init__(*args, **kwargs)
self.fields['pages'].queryset = Page.objects.filter(publisher_is_draft=0)
class Meta:
model = Banners
Take a look at this snippet, you can specify the widget of the field as FilteredSelectMultiple

Django MultipleHiddenInput and ManyToManyField

Can a ManyToManyField inside a ModelForm use a forms.MultipleHiddenInput widget instead of the default forms.MultipleChoiceField? In the example below 'groups' is a ManyToManyField on the Model 'Test':
class TestSelectionForm(forms.ModelForm):
class Meta:
model = Test
fields = ('groups')
widgets = {
'groups': forms.MultipleHiddenInput()
}
def __init__(self, *args, **kwargs):
super(TestSelectionForm, self).__init__(*args, **kwargs)
self.fields['groups'].queryset = Group.objects.filter(...)
But no hidden input fields were rendered for this form. Am I missing something or is it just not possible to use MultipleHiddenInput together with a ManyToManyField? (Should I then just write the HTML for the hidden inputs inside the template or is there a different approach I could use from within the ModelForm)?