Django Dynamic Forms - django

I am using James Bennetts code to create a dynamic form. I have everything working but want to save the data to a database. Has anyone got any code which does this or could show me what the best way to do this would be e.g. how the model should be set up etc?

Override the save() method on your form class:
def save(self):
new_user = User.objects.create_user(username=self.cleaned_data['username'],
email=self.cleaned_data['email'],
password=self.cleaned_data['password1'])
return new_user
(taken from James Bennett's blog at Newforms, part 2)

Related

How does form_valid work in django? How does it compare to save()?

The describtion of this in docs seems very sparse and unclear to me I am asking here. So what is exactly doing the form_valid method here? From what I understand, it gets triggered with POST method and it is kinda calling save() in the last line.
form.instance.entry_author=self.request.user in this line I understand that we are setting the current user to be the author but I dont understand why form referances to instance and also where did the form get from? I suppose its in-built in the form-valid function?
class CreateEntryView(CreateView):
model = Entry
template_name='entries/create_entry.html'
fields = ['entry_title','entry_text']
def form_valid(self, form):
form.instance.entry_author=self.request.user
return super().form_valid(form)
CreateView is a generic view which does a few things for you automatically without you having to write the logic yourself. One of those things is creating a Form. You indicated the model to use, which is Entry. Based on the fields, CreateView creates a form and uses that form.
From the docs: form_valid saves the form instance, sets the current object for the view, and redirects to get_success_url().
You can override this if you have any special requirements. Notice that entry_author is not included in the fields so it will not be present in the form. The idea here is to set the entry_author automatically instead of the user choosing the author in the form.

Where To Override Django CreateView?

I opened up a prior issue on SO regarding manytomany fields not being saved in Django Createview here. Django CreateView With ManyToManyField After troubleshooting most of today, I have found that this code actually works:
class CreateAuthorView(LoginRequiredMixin,CreateView):
def form_valid(self, form):
instance = form.save(commit=False)
instance.save()
instance = form.save()
if instance.access_level == "Custom":
obj = NewAuthor.objects.get(secret=instance.name)
obj.access.add(instance.created_by.id)
print(instance.created_by.id)
print(obj.access.all())
instance = form.save()
obj.save()
form.save_m2m()
instance = form.save()
return super(CreateAuthorView, self).form_valid(form)
When I issue the print(obj.access.all()) I can see in my console that the
obj.access.add(instance.created_by.id)
Line of code actually does exactly what I want it to do...it adds the created_by.id to the access(ManyToManyField) field that I have defined in my model. However, when the record actually gets cut, only the values that the user selected in the form are added to the access field, and the created_by.id never makes it to the database.
Should I be overriding CreateView somewhere else in order for the created_by to take affect? It appears as if my initial update in form_valid is being overwritten is what I suspect. Actually I've proven it because my update is in fact in my console but not making it to the database. Thanks in advance for any thoughts on how best to solve.
I found the answer to my question via this SO question. It turns out you have to override SAVE in the ModelForm in order to save M2M fields. Save Many-To-Many Field Django Forms
This one was tricky.

Django ModelForm hide field from form and use value from url

Thanks in advance for reading this. I can't wrap my head around it and it's getting quite frustrating by now.
We have the following registration form:
class RegistrationForm(forms.ModelForm):
class Meta:
model = Register
fields = ('name', 'company_name')
def clean(self):
if is not self.cleaned_data.get('card').is_available():
raise forms.ValidationError(_('Error'))
The Register model includes a card linked to a Card model. This includes is_available() which functionally works.
Our flow is:
The end user selects the card which lists all registrations for it.
They click the 'Add registration'-button which brings them to cards/{PK}/add.
The Add registration-button is a generic.View. In post(self, request, pk) I have the following code:
form = RegistrationForm(request.POST)
But how do I pass it the contents of Card.objects.get(pk=pk) to it?
I tried:
data = request.POST.copy()
data['card'] = pk
form = RegistrationForm(data)
But I think because card is not included in fields it gets lost somewhere, which makes sense from a sanitize-all-input-point of view, but I would very much like to add the card dynamically, in this case.
Any ideas?
So, just use CreateView and study how it does things using the linked site.
There is no need to use generic.View as it's the basic of basics. You only want to implement all this logic using generic.View to get more familiar with the way things work or if you need some very special form handling.
The short version would be:
from django.views import generic
from myapp.forms import RegistrationForm
class CardCreateView(generic.CreateView):
form_class = RegistrationForm
ModelForm has a save method. The correct way to solve this is to use it with commit=False, that will return an object that hasn’t yet been saved to the database. Then you can alter that object before finally saving it.
This is explained here in the docs
So this is what your code should look like:
form = RegistrationForm(request.POST)
form.save(commit=False)
form.card = Card.objects.get(pk=pk)
form.save_m2m()
save_m2m should be used if your model has many-to-many relationships with other models. In my case, it was a OneToOne, so I used save() instead.
If you use a CreateView instead of the generic View, the snippet above should go into your overridden form_valid method

Django ModelForms: Display ManyToMany field as single-select

In a Django app, I'm having a model Bet which contains a ManyToMany relation with the User model of Django:
class Bet(models.Model):
...
participants = models.ManyToManyField(User)
User should be able to start new bets using a form. Until now, bets have exactly two participants, one of which is the user who creates the bet himself. That means in the form for the new bet you have to chose exactly one participant. The bet creator is added as participant upon saving of the form data.
I'm using a ModelForm for my NewBetForm:
class NewBetForm(forms.ModelForm):
class Meta:
model = Bet
widgets = {
'participants': forms.Select()
}
def save(self, user):
... # save user as participant
Notice the redefined widget for the participants field which makes sure you can only choose one participant.
However, this gives me a validation error:
Enter a list of values.
I'm not really sure where this comes from. If I look at the POST data in the developer tools, it seems to be exactly the same as if I use the default widget and choose only one participant. However, it seems like the to_python() method of the ManyToManyField has its problems with this data. At least there is no User object created if I enable the Select widget.
I know I could work around this problem by excluding the participants field from the form and define it myself but it would be a lot nicer if the ModelForm's capacities could still be used (after all, it's only a widget change). Maybe I could manipulate the passed data in some way if I knew how.
Can anyone tell me what the problem is exactly and if there is a good way to solve it?
Thanks in advance!
Edit
As suggested in the comments: the (relevant) code of the view.
def new_bet(request):
if request.method == 'POST':
form = NewBetForm(request.POST)
if form.is_valid():
form.save(request.user)
... # success message and redirect
else:
form = NewBetForm()
return render(request, 'bets/new.html', {'form': form})
After digging in the Django code, I can answer my own question.
The problem is that Django's ModelForm maps ManyToManyFields in the model to ModelMultipleChoiceFields of the form. This kind of form field expects the widget object to return a sequence from its value_from_datadict() method. The default widget for ModelMultipleChoiceField (which is SelectMultiple) overrides value_from_datadict() to return a list from the user supplied data. But if I use the Select widget, the default value_from_datadict() method of the superclass is used, which simply returns a string. ModelMultipleChoiceField doesn't like that at all, hence the validation error.
To solutions I could think of:
Overriding the value_from_datadict() of Select either via inheritance or some class decorator.
Handling the m2m field manually by creating a new form field and adjusting the save() method of the ModelForm to save its data in the m2m relation.
The seconds solution seems to be less verbose, so that's what I will be going with.
I don't mean to revive a resolved question but I was working a solution like this and thought I would share my code to help others.
In j0ker's answer he lists two methods to get this to work. I used method 1. In which I borrowed the 'value_from_datadict' method from the SelectMultiple widget.
forms.py
from django.utils.datastructures import MultiValueDict, MergeDict
class M2MSelect(forms.Select):
def value_from_datadict(self, data, files, name):
if isinstance(data, (MultiValueDict, MergeDict)):
return data.getlist(name)
return data.get(name, None)
class WindowsSubnetForm(forms.ModelForm):
port_group = forms.ModelMultipleChoiceField(widget=M2MSelect, required=True, queryset=PortGroup.objects.all())
class Meta:
model = Subnet
The problem is that ManyToMany is the wrong data type for this relationship.
In a sense, the bet itself is the many-to-many relationship. It makes no sense to have the participants as a manytomanyfield. What you need is two ForeignKeys, both to User: one for the creator, one for the other user ('acceptor'?)
You can modify the submitted value before (during) validation in Form.clean_field_name. You could use this method to wrap the select's single value in a list.
class NewBetForm(forms.ModelForm):
class Meta:
model = Bet
widgets = {
'participants': forms.Select()
}
def save(self, user):
... # save user as participant
def clean_participants(self):
data = self.cleaned_data['participants']
return [data]
I'm actually just guessing what the value proivded by the select looks like, so this might need a bit of tweaking, but I think it will work.
Here are the docs.
Inspired by #Ryan Currah I found this to be working out of the box:
class M2MSelect(forms.SelectMultiple):
def render(self, name, value, attrs=None, choices=()):
rendered = super(M2MSelect, self).render(name, value=value, attrs=attrs, choices=choices)
return rendered.replace(u'multiple="multiple"', u'')
The first one of the many to many is displayed and when saved only the selected value is left.
I found an easyer way to do this inspired by #Ryan Currah:
You just have to override "allow_multiple_selected" attribut from SelectMultiple class
class M2MSelect(forms.SelectMultiple):
allow_multiple_selected = False
class NewBetForm(forms.ModelForm):
class Meta:
model = Bet
participants = forms.ModelMultipleChoiceField(widget=M2MSelect, required=True, queryset=User.objects.all())

Django Forms and Buttons

I have been working on forms only recently and I am still puzzeld by them.
What I want are standard Forms:
Next Button
Submit Data to Db
Timestamp
Clickable Images with Regions defined where when I click I get to the next page
And
I would like to combine these.
E.g. have a next button + Record the Timestamp.
or
E.g. Click into an Image + Next + Timestamp
If anybody could give me some examples for code that can achieve that or a good online resource on where to get info on that, that would be awesome.
Thanks for the time!!
I'm a little unclear about what you're trying to accomplish, but if you're trying to move data from an HTML form to the database, I'd suggest looking at how to use ModelForms. In a nutshell, you create a model class, like this:
class MyModel(models.Model):
field1 = models.CharField(max_length=50)
Then you create a ModelForm class that references that model:
class MyModelForm(forms.ModelForm):
class Meta:
model = MyModel
You can render an instance of MyModelForm in a view function. Inside of a POST request in that view, you bind the POST data to the form, validate it, and call save() on it to commit it to the database:
if request.method == 'POST':
form = MyModelForm(request.POST)
if form.is_valid():
model_instance = form.save()
This really isn't a question, I'm not exactly sure what you're trying to accomplish.
If you want to use Django forms, start here, or here.
I assume the stuff you mention about a timestamp should probably be an auto_now field in a model. Take a look at this.
The stuff you mention about buttons and click-able images is really just HTML and has nothing to do with Django. I would try Google for that.