I have the following model:
class InfoBox(models.Model):
type = models.ForeignKey(InfoBoxType)
content = models.TextField()
product = models.ForeignKey(Product)
I want to add a css class to my foreignkey. I've tried the following:
forms.py
class InfoBoxForm(ModelForm):
class Meta:
model = InfoBox
widgets = {
'type': ChoiceField(attrs={'class': 'hej'}),
}
I've also tried this but with the same result...
class InfoBoxForm(forms.Form):
type = forms.ChoiceField(
widget=forms.ChoiceField(attrs={'class':'special'}))
admin.py
class InfoBoxInline(admin.StackedInline):
model = InfoBox
extra = 0
form = InfoBoxForm
But im only getting this:
__init__() got an unexpected keyword argument 'attrs'
Im thinking that this would be pretty easy to do, so what am I doing wrong...?
Try:
widgets = {
'type': Select(attrs={'class': 'hej'}),
}
This is the correct widget for your field.
If you don't want to overwrite the widget, you can use:
self.fields['type'].widget.attrs['class'] = 'hej'
in the form's init method
Related
I would like to do the exact same thing as this has the only difference that the default text disappears when the user enters text
name = models.CharField(max_length=16, default="default value")
Exemple :
i'm using this method for my form
class Device(models.Model):
phone_regex = RegexValidator(regex=r'^[0-9]{10}$', message="Error: Format 0611223344")
name = models.CharField(max_length=16, default="default value")
[....cut code....]
class DeviceForm(ModelForm):
class Meta:
model = Device
fields = ['name']
Looks like you want a placeholder value on your field. Add the following code to your Form class.
name = forms.CharField(
label='Name',
widget=forms.TextInput(attrs={'placeholder': 'Type name here...'})
)
The way to do this is to use Django Forms' widget properties. Here, you can change the HTML that will be rendered client-side.
class YourForm(ModelForm):
class Meta:
model = YourModel
fields = ('your', 'fields')
widgets = {
'form_field': forms.TextInput(attrs={'placeholder': "Search Content..."}
}
The above code will render an input tag for the field form_field, and add an HTML attribute of placeholder with the value of Search Content...
Models.py
class Task(models.Model):
online = models.BooleanField(blank=False)
I would like to use django-filter to create the following checkboxes in the form:
[] Online
[] Physical
If form is empty or both are ticked, get Task.objects.all(). If only one is ticked, then do Task.objects.filter('Online'=True/False).
I tried to add the following:
import django_filters
from app.models import Task
from django.db import models
class TaskFilter(django_filters.FilterSet):
online = django_filters.BooleanFilter(name='online', lookup_expr='isnull')
class Meta:
model = Task
fields = ['online']
filter_overrides = {
models.BooleanField: {
'filter_class': django_filters.BooleanFilter,
'extra': lambda f: {
'widget': forms.CheckboxInput,
},
},
}
I tried the following widgets: Select, RadioSelect, CheckboxInput, but it seems I get the same output for all of them. I get a dropdown list e.g.
Online: [ Dropdown to select a value from Yes, No, Unknown ]
You can use choices for that kind of stuff:
TYPE_CHOICES = (
(0, 'Online'),
(1, 'Physical'),
)
class Task(models.Model):
type = models.CharField(
choices=TYPE_CHOICES, default=0, max_length=100
)
objects = TaskQuerySet().as_manager()
Then you can filter it as usual:
Task.objects.filter(type=0).filter(...)
And to make it more easier you may add a custom queryset class:
class TaskQuerySet(models.QuerySet):
def get_online(self):
return self.filter(type=0)
def get_physical(self):
return self.filter(type=1)
That will allow you to do things like:
Task.objects.get_online.filter(...)
Task.objects.get_physical.filter(...)
In the filters.py add:
type = django_filters.MultipleChoiceFilter(field_name='type', choices=CHOICES, widget=forms.CheckboxSelectMultiple)
Filters accept a widget argument, so you if you're manually instantiating filters, you can use:
class TaskFilter(django_filters.FilterSet):
online = django_filters.filters.BooleanFilter(widget=forms.CheckboxInput)
class Meta:
model = Task
fields = ['online']
However, if you're using the meta class to declare fields, then you can override the behavior with filter_overrides
class TaskFilter(django_filters.FilterSet):
filter_overrides = {
models.BooleanField: {
'filter_class': filters.BooleanFilter,
'extra': lambda f: {
widget: forms.CheckboxInput
},
}
}
class Meta:
model = MyModel
fields = ['online']
After a lot of testing though, I came to a conclusion that is usually helpful for default=False model fields.
refer: django-filter issue
I've found a solution to render tags registered in taggit models as choices list by doing:
from taggit.models import Tag
class BlogPost(models.Model):
tags = models.ForeignKey(Tag, on_delete=models.CASCADE, default='')
(i changed something in taggit folder i guess, .... i forgot but i works without any problem )
but i would like to change label name ("Tags" by default)
i tried in forms.py:
imports ...
class PostForm(forms.ModelForm):
tag = [(i, i) for i in Tag.objects.all()]
tags = forms.ChoiceField(label='another label name', choices=tag )
class Meta:
model = BlogPost
fields = ('tags',)
it shows the result i expected but when i save or POST it return a form validation error 'Cannot assign "u'tag name'": "BlogPost.tags" must be a "Tag" instance.'
so can someone handle this and thanks
you need to use pk as key
class PostForm(forms.ModelForm):
tag = [(i.pk, i.title) for i in Tag.objects.all()]
# ^^^^^
tags = forms.ChoiceField(label='another label name', choices=tag )
and may be better solution is to use ModelChoiceField
tags = forms.ModelChoiceField(queryset=Tag.objects.all(), empty_label=None)
For my current Django project I have built a page that uses 3 ModelForms.
The entire page is supposed to let hotels register on the site.
So the first ModelForm is from the Hotel model
Next I want the user to enter 2 forms for images, a main image and a background image. These 2 models are inherited from a basic Image class.
So in this case both images will use the title field, coming from the base class.
Now When I enter a new hotel into the form and also add the 2 images, both images get the same title.
When I take a look at the html code this does makes sense because the input fields both have the same name="title".
For the rest the form works just as expected, it is just this one issue.
Of course I could just take the title field and put them into the child classes as child_class_1_title and child_class_2_title, but that would just break the entire OOP and DRY principle.
How can I make sure that each form pushes the right data into the database?
Thanks a lot in advance.
This is my code for the forms:
class HotelForm(ModelForm):
class Meta:
model = Hotel
fields = ('name', 'address', 'zip_code', 'city', 'website', 'email', 'phone', 'description', 'short_description', 'number_of_rooms', 'facilities',
'activities', 'environment', 'hotel_type')
widgets = {
'facilities': CheckboxSelectMultiple()
}
class HotelGeneralImageForm(ModelForm):
class Meta:
model = HotelGeneralImage
fields = ('title', 'hotel_general_image')
widgets = {
'hotel_general_image': FileWidget()
}
class HotelBackgroundImageForm(ModelForm):
class Meta:
model = HotelBackgroundImage
fields = ('title', 'hotel_background_image')
widgets = {
'hotel_background_image': FileWidget()
}
And the view:
#csrf_exempt
def hotel_registration(request):
if request.method == 'POST':
hotel_form = HotelForm(request.POST, instance=Hotel())
hotel_general_image_form = HotelGeneralImageForm(request.POST, instance=HotelGeneralImage())
hotel_background_image_form = HotelBackgroundImageForm(request.POST, instance=HotelBackgroundImage())
if hotel_form.is_valid() and hotel_general_image_form.is_valid() and hotel_background_image_form.is_valid():
new_hotel = hotel_form.save()
new_hotel_general_image = hotel_general_image_form.save(commit=False)
new_hotel_general_image.hotel = new_hotel
new_hotel_general_image.save()
new_hotel_background_image = hotel_background_image_form.save(commit=False)
new_hotel_background_image.hotel = new_hotel
new_hotel_background_image.save()
return HttpResponseRedirect('registered')
else:
hotel_form = HotelForm(instance=Hotel())
hotel_general_image_form = HotelGeneralImageForm(instance=HotelGeneralImage())
hotel_background_image_form = HotelBackgroundImageForm(instance=HotelBackgroundImage())
context = {'hotel_form': hotel_form,
'hotel_general_image_form': hotel_general_image_form,
'hotel_background_image_form': hotel_background_image_form
}
context.update(csrf(request))
return render_to_response('hotel/hotel-registration-form.html', context)
Form prefix will solve your issue:
hotel_general_image_form = HotelGeneralImageForm(prefix='general', ...)
hotel_background_image_form = HotelBackgroundImageForm(prefix='background', ...)
This way each form will have its own prefix hence will not interfere with other forms.
More in docs - https://docs.djangoproject.com/en/1.9/ref/forms/api/#django.forms.Form.prefix
I have this as my list display in Django admin
list_display = ('product', 'price', 'purchase_date', 'confirmed', 'get_po_number', 'notes')
in models.py:
class PurchaseOrder(models.Model):
notes = models.TextField( null=True, blank= True)
This is what it looks like here:
[1]: http://i.imgur.com/ZyKmpoF.png '
As you can see 'notes' could take up a lot of room, so is there a way I can view/hide that field with the click of a button?
Instead of doing a button, you can resize the field to become smaller.
class PurchaseAdmin(admin.ModelAdmin):
formfield_overrides = {
models.CharField: {'widget': TextInput(attrs={'size':'20'})},
models.TextField: {'widget': Textarea(attrs={'rows':4, 'cols':40})},
}
admin.site.register(PurchaseOrder, PurchaseAdmin)
If you really want to have another button, you can use your custom Inline class to define the fields:
class CustomInline(admin.TabularInline):
readonly_fields = [...'link',...]
# important part which define what "link" looks like
def link(self, instance):
url = # your link to display the note
return mark_safe(u'View Note".format(u=url))
And in your custom admin class, use this Inline class instead:
class PurchaseAdmin(admin.ModelAdmin):
inlines = [CustomInline]