I'm trying to set data in a FormModel after validation. Example of a use case here is if I want to assign a unique number to a form data set if and only if it passes validation. There's some other information I'm trying to tack on too that isn't available prior to validation. I've tried setting in both the view class and in the clean() method of the ModelForm but can't seem to get it right. Each time I get an exception saying that "tag_id" cannot be null. Obviously I'm not setting it in a way that let's form.save() access the data.
Note that in the example below I'm trying to set tag_id in two places to illustrate what I've tried; obviously only one place would be needed if it worked.
What is the proper way to do this?
Model and FormModel:
class Video(models.Model):
tag_id = models.BigIntegerField()
name = models.CharField(max_length=200)
uploaded = models.DateTimeField()
size = models.BigIntegerField()
video_file = models.FileField(upload_to='documents/%Y/%m/%d')
class Meta:
db_table = 'videos'
class VideoForm(forms.ModelForm):
class Meta:
model = Video
fields = ['name', 'video_file']
def clean(self):
self.data['tag_id'] = 10
return self.cleaned_data
The view class:
class Upload(FormView):
template_name = "account/templates/upload.html"
form_class = VideoForm
success_url = reverse_lazy('account:home')
def form_valid(self, form):
form.cleaned_data['tag_id'] = 10
form.save()
return super().form_valid(form)
Exception on form.save():
IntegrityError at /account/upload/
(1048, "Column 'tag_id' cannot be null")
You should set the attribute of the model instance:
form.instance.tag_id = 10
form.save()
Related
I want to alter the data in form_valid before stored.
What I want to do is
The model has the column action in database.
form has the two field model_child_0 model_child_1
Add these two and store in action
These are my source code, but in vain.
My idea is hooking form data in form_valid. Am I wrong?
class MyModelUpdateView(LoginRequiredMixin, UpdateView):
model = MyModel
template_name = 'mymodel_edit.html'
form_class = MyModelForm
def form_valid(self, form):
print(form.cleaned_data['model_child_0'])
print(form.cleaned_data['model_child_1'])
form.cleaned_data['action'] = form.cleaned_data['model_child_0'] + "," + form.cleaned_data['model_child_1']
return super().form_valid(form)
class MyModelForm(forms.ModelForm):
model_child_0 = forms.CharField(required=False)
model_child_1 = forms.CharField(required=False)
class Meta:
model = MyModel
fields = ["model_child_0","model_child_1"]
And also if column name is equal to form name,
There shown previous value automatically.
However I want to set the separated action value, in model_child_0, model_child_1 form as the initial value.
I guess I have to hook before showing template.
Where can I do this?
Solution
I use form.instance instead of form.cleaned_data
form.instance.action = form.cleaned_data['model_child_0'] + "," + form.cleaned_data['model_child_1']
It works.
I'm was creating ModelForm I try to make change the parent class while saving child class fields to the database, in the views.py I made but it didn't save to the database.
here is my model.py
class Table(models.Model):
restaurant = models.ForeignKey(Restaurant, on_delete=models.CASCADE)
book = models.BooleanField(default=False)
class People(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, null=True)
taple = models.OneToOneField(Table, on_delete=models.CASCADE, null=True, blank=True)
#receiver(post_save, sender=User)
def update_people_profile(sender, instance, created, **kwargs):
try:
instance.people.save()
except ObjectDoesNotExist:
People.objects.create(user=instance)
Class People is the child class and Table is the parent class so I'm using People class for making forms. here is my forms.py
class Booking(forms.ModelForm):
class Meta:
model = People
fields = [
'taple',
]
So I want to make True book field in Table class and save it to the database when saving Booking form. here is my views.py
def booking(request):
if request.method == 'POST':
try:
people_instance = People.objects.get(user=request.user)
except Table.DoesNotExist:
people_instance = People(user=request.user)
form = Booking(request.POST, instance=people_instance)
if form.is_valid():
user = form.save(commit=False)
user.taple.booking = True
user.refresh_from_db()
user.user = request.user
user.taple = form.cleaned_data.get('taple')
user.save()
print(user.taple.booking, user.taple.id)
return redirect('booked')
else:
form = Booking()
return render(request, 'main/booking.html', {'form': form})
Any Idea?
What I understand from the snippets is that you want to be able to record if a table is booked (book Boolean Field in your Table model and if so by whom, which is the object of your People model.
If my understanding is correct, then I don't think you really need a join table (People model). Instead, I would change your model as follow:
class Table(models.Model):
restaurant = models.ForeignKey(Restaurant, on_delete=models.CASCADE)
booked_by = models.OneToOneField(User, null=True, on_delete=models.SET_NULL, related_name='table_booked')
#property
def is_booked(self):
# This returns True if booked_by is set, False otherwise
return self.booked_by_id is not None
This way you don't need the People model. The property decorator will allow you to use is_booked as a calculated field.
Also, note the related name which will be used in the form:
class BookingForm(forms.ModelForm):
table_booked = forms.ModelChoiceField(queryset=Table.objects.filter(booked_by__isnull=True))
class Meta:
model = User
fields = ['table_booked',]
In the form, you will see that we define a custom queryset for table_booked. THe aim is to filter for free tables only.
Then you can hopefully simplify as well your view as follow:
Update:
As table_booked is a reverse foreign key, we actually need to save the table object which contains the relation. Here is the modified view:
#login_required
def booking(request):
form = BookingForm(request.POST or None, instance=request.user)
if form.is_valid():
user = form.save(commit=False)
tbl = form.cleaned_data['table_booked']
tbl.booked_by = request.user
tbl.save()
user.save()
print(request.user.table_booked.id, request.user.table_booked.is_booked)
return redirect('/')
return render(request, 'booking/booking.html', {'form': form})
Note: I haven't tested the code so there could be some typos but that should help you getting started.
I have got the code from https://github.com/adandan01/mybook, the code is working fine, even when I have updated it to Django 2. It's very simple project for adding a person in a form, and his/her relatives in the inline form. Everything works but when I add a relative name and forget to add his relationship, and submitted the form, unfortunately, that record will not pass the validation but will give no error messages as well. Django will ignore the entire record. For example, the record for Hawra in the image, will not be saved and Django will remove it. For this simple App there are only two fields to be filled (name and relationship), but I'm working on app with 8 fields, and it will be difficult to lose the data. is there any way to make django do the validation in the formset/subform as long as any fields have data and will ask the user to fill all required fields?
models.py:
class Profile(models.Model):
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
created_date = models.DateTimeField(default=timezone.now)
def get_absolute_url(self):
return reverse('profile-update', kwargs={'pk': self.pk})
def __unicode__(self):
return "%s %s" % (self.first_name, self.last_name)
class FamilyMember(models.Model):
profile = models.ForeignKey(Profile, on_delete=models.PROTECT)
name = models.CharField(max_length=100)
relationship = models.CharField(max_length=100)
form.py
class ProfileForm(ModelForm):
class Meta:
model = Profile
exclude = ()
class FamilyMemberForm(ModelForm):
class Meta:
model = FamilyMember
exclude = ()
FamilyMemberFormSet = inlineformset_factory(Profile, FamilyMember,
form=FamilyMemberForm, extra=1)
views.py
class ProfileCreate(CreateView):
model = Profile
fields = ['first_name', 'last_name']
class ProfileFamilyMemberCreate(CreateView):
model = Profile
fields = ['first_name', 'last_name']
success_url = reverse_lazy('profile-list')
def get_context_data(self, **kwargs):
data = super(ProfileFamilyMemberCreate, self).get_context_data(**kwargs)
if self.request.POST:
data['familymembers'] = FamilyMemberFormSet(self.request.POST)
else:
data['familymembers'] = FamilyMemberFormSet()
return data
def form_valid(self, form):
context = self.get_context_data()
familymembers = context['familymembers']
with transaction.atomic():
self.object = form.save()
if familymembers.is_valid():
familymembers.instance = self.object
familymembers.save()
return super(ProfileFamilyMemberCreate, self).form_valid(form)
I found the solution here django inline_formset - form.empty_permitted = False doesn't work
I had to add the following code before if (familymembers.is_valid():...) in the create and update class, so, now Django will show the error if I entered data in the Name field only and will tell me the Relationship field is required.
if familymembers.is_valid() == False:
return self.render_to_response(self.get_context_data(form=form,familymembers=familymembers ))
I am new on Django, I am using Django 1.6.1 and in one of my form I get the next error
This field is required.
I don't understand why I get this error, my form is very basic, so my Model, the statement who launch this error is form.is_valid(), but I don't know why.
Some help would be apreciate.
my views is:
if request.method == 'POST':
form = campoImagenForm(request.POST, request.FILES)
if form.is_valid():
articulo = form.save(commit=False)
articulo.item = item
articulo.atributo = atributo
articulo.save()
return HttpResponseRedirect('/workproject/' + str(project.id))
my Form is:
class campoImagenForm(forms.ModelForm):
class Meta:
model = campoImagen
fields = ('imagen',)
my Model is:
class campoImagen(models.Model):
item = models.ForeignKey(ItemBase)
atributo = models.ForeignKey(TipoItem)
imagen = models.ImageField(verbose_name='Imagen', upload_to='archivos')
version = models.IntegerField()
I don't have a clue of why I get such an error.
At first, in your model make your fields empty and nullable:
class campoImagen(models.Model):
# [...]
imagen = models.ImageField(verbose_name='Imagen', upload_to='archivos', null=True, blank=True)
Then in your model form alter the required parameter of your field:
class campoImagenForm(forms.ModelForm):
class Meta:
model = campoImagen
fields = ('imagen',)
def __init__(self, *args, **kwargs):
super(campoImagenForm, self).__init__(*args, **kwargs)
self.fields['imagen'].required = False
The model part is, however, obligatory. Otherwise you might be getting database inconsistency errors.
Keep in mind that blank and null are different things. They do, however, work well together and should give you what you want.
In forms.py
class campoImagenForm(forms.ModelForm):
imagen = forms.ImageField(verbose_name='Imagen', upload_to='archivos', required = False)
class Meta:
model = campoImagen
fields = ('imagen',)
These are my models:
class war(models.Model):
title = models.CharField(max_length=20)
class option(models.Model):
warval =models.ForeignKey(war)
value = models.CharField(max_length=10)
class warform(ModelForm):
class Meta:
model = war
class option_form(ModelForm):
class Meta:
model = option
exclude = ('warval')
And this is the view which handles creation of option:
def warhandler(request,war_id):
f=modelformset_factory(option,form=option_form)
wobj=get_object_or_404(war,pk=war_id)
if request.method == 'POST':
formset = f(request.POST,queryset=option.objects.filter(warval=wobj))
if formset.is_valid():
formset.save()
return HttpResponse("Saved!check your model")
else:
return render_to_response("formset.html",RequestContext(request,{"set":formset}))
else:
formset = f(queryset=option.objects.filter(warval=wobj))
print(formset)
return render_to_response("formset.html",RequestContext(request,{"set":formset,"war":wobj}))
So when I submit the form to this view,I get the following error:
Exception Type: IntegrityError
Exception Value: hit_option.warval_id may not be NULL
I know what this error is and why it is coming but how can I remove this error?
As you realize, this is because you're not setting the foreign key value anywhere. You probably want to use inlineformset_factory which will take care of setting the FK to the parent war object for you.
Make warval a non-required field with
class option(models.Model):
warval =models.ForeignKey(war, null=True)
value = models.CharField(max_length=10)
or define formset.warval before saving, make a default value...
edit:
read here and here for using subset of fields on a form and saving.