Add extra fields to a ModelAdmin form - python

So, I have a ModelAdmin that I need to add extra fields to. These fields do not exist on the model, but will be dynamically added to a custom ModelForm through the init method, and logic inside clean will handle the returned data on save.
I can't seem to find any solid information related to adding custom non-model fields to a ModelAdmin form. The closest I have come is by overriding get_fields on the ModelAdmin class and updating self.form.declared_fields with the new fields I'd like to add.
This just doesn't feel very clean to me and I was curious if there was a better way to add new fields to a ModelAdmin dynamically?

You can also use this way to add fields not in the models.py
in admin.py:::
from django.utils.translation import gettext as _
class TrialAdmin(admin.ModelAdmin):
fields = (..., 'non_model_field',...,)
def non_model_field(self, instance):
# this method name should be same as new field name defied above in fields.
# [what u need to do here]
non_model_field.short_description = _("non_model_field")
#This value 'short_description' defines what the field name is shown as in admin similar to 'verbose_name' in models.
admin.site.register(..., TrialAdmin)

Related

Why "class Meta" is necessary while creating a model form?

from django import forms
from .models import NewsSignUp
class NewsSignUpForm(forms.ModelForm):
class Meta:
model = NewsSignUp
fields = ['email', 'first_name']
here
This code works perfectly fine. But, when I remove "class Meta:" as below, it throws a ValueError saying "ModelForm has no model class specified."
from django import forms
from .models import NewsSignUp
class NewsSignUpForm(forms.ModelForm):
model = NewsSignUp
fields = ['email', 'first_name']
Can someone please give an explanation? :(
You are creating a ModelForm subclass. A model form has to have a model to work from, and the Meta object configures this.
Configuration like this is grouped into the Meta class to avoid name clashes; that way you can have a model field in your form without that interfering with the configuration. In other words, by using class Meta: you get a nested namespace used just to configure the ModelForm in relation to the model.
The namespace for the ModelForm class body itself then (outside Meta) is reserved for the form fields themselves, as well as form methods. You'd normally just let ModelForm generate those fields from your model, but you can, in principle, add fields to this. Another reason to put fields in the class is to completely replace any of the generated fields with your own version.
From the Model Forms documentation:
ModelForm is a regular Form which can automatically generate certain fields. The fields that are automatically generated depend on the content of the Meta class and on which fields have already been defined declaratively. Basically, ModelForm will only generate fields that are missing from the form, or in other words, fields that weren’t defined declaratively.
Fields defined declaratively are left as-is, therefore any customizations made to Meta attributes such as widgets, labels, help_texts, or error_messages are ignored; these only apply to fields that are generated automatically.
Because ModelForm expects the configuration to be set under the Meta name, you can't just remove that and put model and fields in the ModelForm class itself; that's just the wrong place.

Using a FormSet in a ClassBasedView in django

I'm trying to set up a django form consisting solely of a formset. In forms.py I have:
class StudentEnrolmentForm(forms.ModelForm):
school_class = forms.ModelChoiceField(SchoolClass.objects.currently_enrolling())
class Meta:
model = StudentApplication
fields = []
StudentEnrolmentFormSet = modelformset_factory(StudentApplication, StudentEnrolmentForm, extra=0)
but I'm unclear how to incorporate the FormSet into a CBV (In this case I've chosen a FormView). In this case I'm basically displaying a table of students, and allowing the operator to assign each student to a class. I only want a single 'submit' button at the end of the page.
If you will take a look on sources of Django views and check how FormView is working, you find that it just overrides default get and post methods of base View class and adds some additional methods for the form handling.
So you can:
try to assign your formset to the form_class field of your view and
play around. Probably you will have to override some additional
methods;
take a look on https://github.com/AndrewIngram/django-extra-views;
if options #1 and #2 causes too much pain - use default View

Django form exclude options in select field

I have one model that has a ManyToMany Field (let's call it "Options") with another Model
When I create the ModelForm it displays all options.
Is there any way to exclude some option values or to show only some of them?
Here is an example:
models.py
class Options (model.Models):
name = ...
...
class Anything (model.Models):
...
options = ManyToManyField(Options)
values of "Options" in my DB:
["OK",
"OK_2",
"NOT_OK",
"OK_3,
"NOT_OK_2"]
Let's say that I need to show ONLY the "OK" values and hide or not to show the "NOT_OK" values.
Is there any way to do this with ModelForms?
You certainly can filter the queryset for a foreign key field or m2m on the related model by using a Form or more commonly a ModelForm.
The reason doing this at form level is useful is because that filtering could well be based on business logic which is not applicable in all cases and so allows more flexibility than defining it against the model for example.
While you can do this while defining the form fields it is best to do it once the form has been constructed and so it takes place at runtime and not compile time (I have just experienced a few interesting occasions where this has caused me some issues, however that was an earlier version of Django!)
The following ModelForm would do the job:
class AnythingForm(ModelForm):
options = forms.MultipleChoiceField()
def __init__(self, **kwargs):
super(AnythingForm, self).__init__(self, **kwargs)
self.fields['options'].queryset = Option.objects.filter({pass in your filters here...})
class Meta:
model = Anything
You can pass the limit_choices_to parameter to your ManyToMany field:
from django.db.models import Q
class Anything (models.Model):
options = models.ManyToManyField(Options,
limit_choices_to=Q(name__startswith='OK'))
In django 1.7 you can even pass a callable in case if list of choices should be changed dynamically.

Django Form with extra information

I'm have a model that has some fields that should never be edited, such as "date created." I want to display a form that has some text fields with the extra information, probably just the data itself in a <span>. A person can edit a few fields and at the same time they can see the extra information about the instance of the model, such as created, last modified, or even related objects in the database for this object. Is there a way to do this using Django's built in framework, or will I have to create my own custom form by passing the whole object to a template?
If this needs more clarification, feel free to ask.
The best way I know to do this is to initialize the fields before you pass the form to the template by passing an initial dictionary to the form or by passing a instance object to the form.
You should then make sure that the fields are disabled, or you should make them hidden fields and then display the fields as regular text.
Most importantly, if you're passing data to the client that will then be sent back in a form, you should make sure that the data coming in is the same as the data that went out (for security's sake). Do this with at clean_[field] function on the Form. It should look like the following.
class MyForm(forms.ModelForm):
class Meta:
model = MyModel
def clean_date_created(self):
if self.cleaned_fields['date_created'] != self.instance.date_created:
raise ValidationError, 'date_created has been tampered'
self.cleaned_fields['date_created']
[Edit/Addendum] Alternatively, you can pass the data directly to your template to render separately, and then tack on the data to your form after you get it back into your view. It should go something like this:
def recieve_form(request, ...):
...
f = MyForm(request.POST, instance=a)
new_model_instance = f.save(commit=False)
new_model_instance.date_created = <whatever>
new_model_instance.save()
To do that I usually customize the form in order for the widget to be read only, like the following:
class MyForm(forms.ModelForm):
class Meta:
model = MyModel
widgets = {
'my_field': forms.TextInput(attrs={'disabled':'disabled'}),
}
This will output a read only text field but I guess you can also create a Widget that will just output a <div> or a <p> (or whatever you need)

Update only some field with create_update.update_object

I've defined a models.py with a "FirstClass" which contains a ForeignKey relathionship to "SecondClass". The relathionship can't be Null.
The SecondClass is very expansive (90.000 records), and when i display the FirstClass html form, it requires too many time generating the "select box" field.
Therefore, when I let user update the object (I use create_update.update_object generic view), i don't want to display and update the value of the foreignkey field, but i don't know how to do this...
Create a ModelForm and pass it into the view, according to the docs.
Since the foreign key should always exist upon creation, it's safe to ignore it in the update.
class MyModelForm(forms.ModelForm):
class Meta:
model = FirstClass
exclude = ('SecondClass',)
# urls.py
(r'^foo/(?P<object_id>\d+)/$','django.views.generic.create_update.update_object',
{'form_class': MyModelForm})

Categories

Resources