I have two form classes, each of which are a ModelForm of the same model. One is called ProjectForm and the other is called AdminProjectForm. ProjectForm has a number of fields excluded, AdminProjectForm does not.
In my views, I'm using two classes, Edit and AdminEdit. Edit uses the Django generic editing view of UpdateView and has the form_class set to ProjectForm. AdminEdit is a inherits Edit and has the form_class set to AdminProjectEdit.
One would think this would mean that the form generated by AdminEdit would thus show the fields that are excluded on Edit. This part is working correctly - the form fields are drawn perfectly fine (and not drawn on Edit. However, when submitting the AdminEdit form, any field excluded in ProjectForm is stripped and not saved. Any suggestions?
Here's my forms.py:
class ProjectForm(ModelForm):
class Meta:
model = Project
exclude = ('field1', 'field2', 'field3', 'qualifies_for_judging', 'reason_for_disqualification', 'finalist', 'hashtag')
class AdminProjectForm(ModelForm):
class Meta:
model = Project
exclude = ()
And my views.py:
class Edit(UpdateView):
model = Project
form_class = ProjectForm
class AdminEdit(Edit):
model = Project
form_class = AdminProjectForm
Related
I am building an app where managers can create a private webpage, they need to add people manually in order for them to access the page.
I don't want the managers to see all of the users, So I would like to render only the PK in the list.
My views.py
class HotelCreateView(LoginRequiredMixin, CreateView):
model = Hotel
form_class = HotelForm
def form_valid(self, form):
form.instance.manager_hotel = self.request.user
return super().form_valid(form)
forms.py
from django.db import models
from django.forms import ModelForm
from .models import Hotel
class ColleagueChoiceField(ModelChoiceField):
def label_from_instance(self, obj):
return obj.get_pk()
class HotelForm(models.Model):
ColleagueModelChoiceField(queryset=Colleague.objects.filter(pk))
You're setting fields on your CreateView, so you're letting Django generate the ModelForm automatically for you. The form uses a ModelMultipleChoiceField, which derives from ModelChoiceField, described here.
If you read the last paragraph of that section, you'll see that the display values for such a field are coming from the model's __str__ method, or you can override this with the label_from_instance() method.
That's therefore what you need to do, override this method on the ModelMultipleChoiceField. But to do that, you need to specify your own form.
So:
Create your own ModelForm for your Hotel model (HotelForm).
Create a subclass of ModelMultipleChoiceField (ColleagueChoiceField) and override the label_from_instance() method to display the pk.
Set the colleagues field on the HotelForm to be a ColleagueChoiceField.
Remove the fields attribute on your view and set the form_class to your HotelForm instead.
I am a beginner programming in Django framework and I am learning how to implement a CreateView (a class based view for creating a form based on a model) in my views.py file.
No, you don't the view will automatically create a model form for you, but you have to option of overwriting it.
Let's assume you have MyModel, you can do this:
from myapp.models import MyModel
# views.py
class MyCreateView(CreateView):
model = MyModel
fields = ['something', 'somethingelse'] # these are fields from MyModel
If you do not specify the fields Django will throw an error.
If you want to customize your form validation in some way, you can do this:
# forms.py
class MyForm(ModelForm):
class Meta:
model = MyModel
fields = ['something'] # form fields that map to the model
# ... do some custom stuff
# views.py
class MyCreateView(CreateView):
model = MyModel
form_class = MyForm
Notice that we are not specifying the fields anymore on MyView because if we would it will also throw an error, and the reasons is because the view will fetch the fields from the form.
More information: https://docs.djangoproject.com/en/2.1/topics/class-based-views/generic-editing/
Code that handles the form_class: https://github.com/django/django/blob/master/django/views/generic/edit.py#L74
You don't need to create a ModelForm, you just need to specify the model in the model attribute, e.g. for an Author model set model = Author.
CreateView uses the ModelFormMixin, which uses this model attribute to handle the ModelForm:
from django.views.generic.edit import CreateView
from myapp.models import Author
class AuthorCreate(CreateView):
model = Author
fields = ['name']
See more here: https://docs.djangoproject.com/en/2.1/ref/class-based-views/mixins-editing/#django.views.generic.edit.ModelFormMixin.model
I still didn't understood the UpdateView. Where does it fetch the form from?
It has exactly the fields declared in the model, but doesn't use the form defined in forms.py.
I did however follow the answer given at:
How does one use a custom widget with a generic UpdateView without having to redefine the entire form?
In my case I use an IntegerField in model.py and use Radiobuttons in the Form.
So what the UpdateView does is giving me an IntegerField instead of a ChoiceField. Even when I assigned the RadioSelect Widget or a Choice Field:
The View:
class UpdateEinflussideen(UpdateView):
model = Einflussideen
EINFLUSS = [(10,'hoch'),(4,'mittel'),(1,'gering')]
form_class = forms.models.modelform_factory(Einflussideen,
widgets={'einfluss': forms.ChoiceField(
choices=EINFLUSS, widget=forms.RadioSelect())},
)
template_name = 'verbrauchererfassung/update_einflussideen.html'
success_url = reverse_lazy('verbraucher')
The Model:
class Einflussideen(models.Model):
idee = models.CharField(max_length=100)
einfluss = models.IntegerField()
verbraucher = models.ForeignKey(Verbraucher)
Variables in python are case-sensitive. Change the atribute form_Class to the form_class. Also the widgets argument should contain a dict with the Widget instances in the values:
form_class = forms.models.modelform_factory(Einflussideen,
widgets={'einfluss': forms.RadioSelect(choices=EINFLUSS)})
Apparently Django's ModelAdmin/ModelForm doesn't allow you to use save_m2m() if there's an intermediate through table for a ManyToManyField.
models.py:
from django.db import models
def make_uuid():
import uuid
return uuid.uuid4().hex
class MyModel(models.Model):
id = models.CharField(default=make_uuid, max_length=32, primary_key=True)
title = models.CharField(max_length=32)
many = models.ManyToManyField("RelatedModel", through="RelatedToMyModel")
def save(self, *args, **kwargs):
if not self.id:
self.id = make_uuid()
super(GuidPk, self).save(*args, **kwargs)
class RelatedModel(models.Model):
field = models.CharField(max_length=32)
class RelatedToMyModel(models.Model):
my_model = models.ForeignKey(MyModel)
related_model = models.ForeignKey(RelatedModel)
additional_field = models.CharField(max_length=32)
admin.py:
from django import forms
from django.contrib import admin
from .models import MyModel
class RelatedToMyModelInline(admin.TabularInline):
model = MyModel.many.through
class MyModelAdminForm(forms.ModelForm):
class Meta:
model = MyModel
class MyModelAdmin(admin.ModelAdmin):
form = MyModelAdminForm
inlines = (RelatedToMyModelInline, )
admin.site.register(MyModel, MyModelAdmin)
If I save MyModel first and then add a new related through model via the inline it works fine, but if I try to set the inline while also adding data for a new MyModel, I get the Django Admin error "Please correct the error below." with nothing highlighted below.
How can I have it save MyModel and then save the inline intermediary models after? Clearly Django can save the through model once it has saved MyModel - so I'm just looking for a hook into that. I tried overriding the form's save() method by calling save_m2m() after calling instance.save(), but apparently that doesn't work for M2Ms with a through table.
I'm using Django 1.2, but this is still an issue in 1.3.
UPDATE: Well, I made a test app like above to isolate the problem, and it appears that it works as expected, correctly saving the M2M intermediary object after saving the MyModel object... as long as I let Django automatically create the MyModel.id field when running python manage.py syncdb - once I added the GUID id field, it no longer works.
This smells more and more like a Django bug.
In your MyModelAdmin you might try overriding the save_formset method. This way you can choose the order in which you save.
I have a model named Project which has a m2m field users. I have a task model with a FK project. And it has a field assigned_to. How can i limit the choices of assigned_to to only the users of the current project?
You could do this another way, using this nifty form factory trick.
def make_task_form(project):
class _TaskForm(forms.Form):
assigned_to = forms.ModelChoiceField(
queryset=User.objects.filter(user__project=project))
class Meta:
model = Task
return _TaskForm
Then from your view code you can do something like this:
project = Project.objects.get(id=234)
form_class = make_task_form(project)
...
form = form_class(request.POST)
You need to create a custom form for the admin.
Your form should contain a ModelChoiceField in which you can specify a queryset parameter that defines what the available choices are. This form can be a ModelForm.
(the following example assumes users have an FK to your Project model)
forms.py
from django import forms
class TaskForm(forms.ModelForm):
assigned_to = forms.ModelChoiceField(queryset=Users.objects.filter(user__project=project))
class Meta:
model = Task
Then assign the form to the ModelAdmin.
admin.py
from django.contrib import admin
from models import Task
from forms import TaskForm
class TaskAdmin(admin.ModelAdmin):
form = TaskForm
admin.site.register(Task, TaskAdmin)