When I'm using model forms I can exclude a field from showing by using a Meta class and exclude. However, with standard none model forms this does not work. I want to hide a field call amount and populate on init. How?
amount = forms.FloatField()
well I have tried to first exclude like this...
class Meta:
exclude = ('amount',)
but this does not seems to work on none model forms.
Use forms.ModelForm to override the init
class YourForm(forms.ModelForm):
amount = forms.FloatField()
def __init__(self, *args, **kwargs):
super(YourForm, self).__init__(*args, **kwargs)
self.fields['amount'].widget = forms.HiddenInput()
self.fields['amount'].initial = ''
Related
I have a form something like this:
class ExampleForm(forms.ModelForm):
class Meta:
model = Example
fields = ('field1','field2')
Now, I want to add an additional field if an instance is passed to it. Which means that, if I create an form with an instance (for editing that object) I want to change the fields being displayed.
I know I can use create an another form for this purpose and create an instance of that rather than using this. However, is there a way to do this in this same form?
Override the __init__ method, and modify self.fields when instance is passed to the form.
class ExampleForm(forms.ModelForm):
class Meta:
model = Example
fields = ('field1','field2')
def __init__(self, *args, **kwargs):
super(ExampleForm, self).__init__(*args, **kwargs)
if kwargs.get('instance'):
self.fields['field3'] = forms.CharField()
I have my models.py like this:
class Category(models.Model):
user = models.ForeignKey(User)
name = models.CharField(max_length=256, db_index=True)
class Todo(models.Model):
user = models.ForeignKey(User)
category = models.ForeignKey(Category)
...
And I want to limit choices of Category for Todo to only those ones where Todo.user = Category.user
Every solutuion that I've found was to set queryset for a ModelForm or implement method inside a form. (As with limit_choices_to it is not possible(?))
The problem is that I have not only one model with such limiting problem (e.g Tag, etc.)
Also, I'm using django REST framework, so I have to check Category when Todo is added or edited.
So, I also need functions validate in serializers to limit models right (as it does not call model's clean, full_clean methods and does not check limit_choices_to)
So, I'm looking for a simple solution, which will work for both django Admin and REST framework.
Or, if it is not possible to implement it the simple way, I'm looking for an advice of how to code it the most painless way.
Here what I've found so far:
To get Foreignkey showed right in admin, you have to specify a form in ModelAdmin
class TodoAdminForm(ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['category'].queryset = Category.objects.filter(user__pk=self.instance.user.pk)
#admin.register(Todo)
class TodoAdmin(admin.ModelAdmin):
form = TodoAdminForm
...
To get ManyToManyField showed right in InlineModelAdmin (e.g. TabularInline) here comes more dirty hack (can it be done better?)
You have to save your quiring field value from object and then manually set queryset in the field. My through model has two members todo and tag
And I'd like to filter tag field (pointing to model Tag):
class MembershipInline(admin.TabularInline):
model = Todo.tags.through
def get_formset(self, request, obj=None, **kwargs):
request.saved_user_pk = obj.user.pk # Not sure if it can be None
return super().get_formset(request, obj, **kwargs)
def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
if db_field.name == 'tag':
kwargs['queryset'] = Tag.objects.filter(user__pk=request.saved_user_pk)
return super().formfield_for_foreignkey(db_field, request, **kwargs)
And finally, to restrict elements only to related in Django REST framework, I have to implement custom Field
class PrimaryKeyRelatedByUser(serializers.PrimaryKeyRelatedField):
def get_queryset(self):
return super().get_queryset().filter(user=self.context['request'].user)
And use it in my serializer like
class TodoSerializer(serializers.ModelSerializer):
category = PrimaryKeyRelatedByUser(required=False, allow_null=True, queryset=Category.objects.all())
tags = PrimaryKeyRelatedByUser(required=False, many=True, queryset=Tag.objects.all())
class Meta:
model = Todo
fields = ('id', 'category', 'tags', ...)
Not sure if it actually working in all cases as planned. I'll continue this small investigation.
Question still remains. Could it be done simplier?
I do not understand the official document about exclude .
Set the exclude attribute of the ModelForm‘s inner Meta class to a list of fields to be excluded from the form.
For example:
class PartialAuthorForm(ModelForm):
class Meta:
model = Author
exclude = ['title']
Since the Author model has the 3 fields name, title and birth_date, this will result in the fields name and birth_date being present on the form.
My understanding is as follows: django form save method will save all form data.If one set exclude =('something',) , 'something' field will not show on frontend and wouldn't be save while calling form save method.
But when I do as the document saying, 'something' field still show.What's the matter?
I also want to add some fields to a form for validating which can show on frontend without saving.It is stange that I find nothing about this need.
**update**
my code :
class ProfileForm(Html5Mixin, forms.ModelForm):
password1 = forms.CharField(label=_("Password"),
widget=forms.PasswordInput(render_value=False))
password2 = forms.CharField(label=_("Password (again)"),
widget=forms.PasswordInput(render_value=False))
captcha_text = forms.CharField(label=_("captcha"),
widget=forms.TextInput())
captcha_detext = forms.CharField(
widget=forms.HiddenInput())
class Meta:
model = User
fields = ("email", "username")
exclude = ['captcha_text']
def __init__(self, *args, **kwargs):
super(ProfileForm, self).__init__(*args, **kwargs)
..........
def clean_username(self):
.....
def clean_password2(self):
....
def save(self, *args, **kwargs):
"""
Create the new user. If no username is supplied (may be hidden
via ``ACCOUNTS_PROFILE_FORM_EXCLUDE_FIELDS`` or
``ACCOUNTS_NO_USERNAME``), we generate a unique username, so
that if profile pages are enabled, we still have something to
use as the profile's slug.
"""
..............
def get_profile_fields_form(self):
return ProfileFieldsForm
if exclude only affect the model defined under class Meta , so exclude = ['captcha_text'] would not work?
exclude = ['title'] will exclude the field from the form, not from the model.
form.save() will try to save the model instance with the available for fields, but model might throw any error pertaining to the missing field.
To add extra fields in model form, do this:
class PartialAuthorForm (ModelForm):
extra_field = forms.IntegerField()
class Meta:
model = Author
def save(self, *args, **kwargs):
# do something with self.cleaned_data['extra_field']
super(PartialAuthorForm, self).save(*args, **kwargs)
But make sure there is no field called "PartialAuthorForm" in the model Author.
First, the reason why your title field is still displayed must be somewhere in your view. Be sure that you create your (unbound) form instance like this:
form = PartialAuthorForm()
and try this simple rendering method in the template
{{ form.as_p }}
Second, it should be no problem to add extra fields to a model form, see e.g. this post.
I have a model with an __init__ method:
class Foo(models.Model):
name = models.CharField(max_length=50)
def __init__(self, *args, **kwargs):
self.bar = kwargs.pop('bar', False)
super(Foo, self).__init__(*args, **kwargs)
if self.bar:
# do something
pass
Now, i need to create a specific ModelForm:
class FooForm(ModelForm):
class Meta:
model = Foo(bar='something')
fields = ('name',)
That does not work apparently:
TypeError: 'Foo' object is not callable
Is there any way i can overcome this?
Update
More information on what i want to achieve: I have an Image model with an ImageField. It has different storage methods depending on the form that uses it.
The model:
class Image(models.Model):
image = models.ImageField(upload_to=imageUploadTo)
user = models.ForeignKey('auth.User')
def __init__(self, *args, **kwargs):
self.overwrite = kwargs.pop('overwrite', False)
super(Image, self).__init__(*args, **kwargs)
if self.overwrite:
self.image.storage = OverwriteStorage()
Now i want to be able to create forms that overwrite the old image and forms that use the default behavior. What's the best way to achieve this?
No, that's not how it works at all, and this has nothing to do with your custom init. You don't call things inside Meta. In your case, you pass the parameter when you initialize the form in your view.
Customizing a queryset of a form field in Django isn't a hard job. Like this
But, assuming I have the following models:
#models.py
class Work(Model):
name = models.CharfField(...)
#some fields
class Gallery(Model):
work = models.ForeignKey(Work)
class Photo(Model):
gallery = models.ForeignKey(Gallery)
class StageOfWork(Model):
work = models.ForeignKey(Work)
gallery = models.ForeignKey(Gallery)
#some fields
And an admin.py like this
#admin.py
class StageOfWorkAdmin(admin.TabularInline):
model = StageOfWork
form = StageOfWorkForm
extra = 1
class WorkAdmin(admin.ModelAdmin):
inlines = [EtapaObraAdmin]
I have this problem: when I edit a Work, exists many Form Inlines of StageOfWorks, these StageOfWorks inline forms have a Gallery selector.
I need to customize de queryset of this Galleries like this:
class StageOfWorkForm(ModelForm):
def __init__(self, *args, **kwargs):
super(StageOfWorkForm, self).__init__(*args, **kwargs)
if 'instance' in kwargs:
self.fields['gallery'].queryset = Gallery.objects.filter(work__id=self.instance.work.id)
But this only works in the Forms who is editing forms. I need to get a work id in context of init method to do the right queryset anyway.
How could I do that?
The only way I have been able to do it is pass in the data you need into the instantiaton of the form class.
i.e., in your view:
def view(request):
...
work = <whatever>
form = StageOfWorkForm(work, request.POST)
...
Then, your form needs to the work object:
class StageOfWorkForm(ModelForm):
def __init__(self, work, *args, **kwargs):
super(StageOfWorkForm, self).__init__(*args, **kwargs)
self.fields['gallery'].queryset = work.gallery_set.all()
I haven't done this exact thing, but I did something similar. I used the Smart Selects Django plugin. This can be found here: https://github.com/digi604/django-smart-selects
I've use this to a filtered select in the admin, but it was in a regular model, not an inline, but it is quite possible that the plugin works in inlines too. I'd at least check it out.
Hailey