Django somehow looses updated value in form - django

I have one big object - report of users trips. I have provided interface for user to edit some fields in that report one by one. Since the user is can edit one field at a time i have created method which creates modelform based on which field user wants to edit.
It works like that:
def createFieldEditorForm(client, field, report_id, request = None):
from django.shortcuts import get_object_or_404
report_instance = get_object_or_404(DriveReport, id = report_id)
class FieldEditorForm(forms.ModelForm):
class Meta:
model = DriveReport
fields = ['id', ]
id = forms.IntegerField(label = _(u"ride_id"), widget=forms.HiddenInput())
def __init__(self, *args, **kwargs):
super(FieldEditorForm, self).__init__(*args, **kwargs)
self.build_fields()
def build_fields(self):
if field == 'driver_id':
driver_query = Driver.objects.filter(client = client)
choices = [('', u'------'),]
for driver in driver_query:
choices.append((driver.id, driver.name))
self.fields[field] = forms.IntegerField(
label = _(u"Drivers"),
widget = forms.Select(
choices = choices,
attrs = {'data-name':'driver_id'} ),
required = False)
elif field == 'project_id':
area_query = Area.objects.filter(client = client)
choices = [('', u'------'),]
for area in area_query:
choices.append((area.id, area.name))
self.fields[field] = forms.IntegerField(
label = _(u"Projects"),
widget = froms.Select(
choices = choices,
attrs = {'data-name':'project_id'} ),
required = False)
elif field == 'trip_type':
self.fields[field] = forms.CharField(
label = _(u"Projects"),
widget = forms.Select(
choices = [(key, value) for key, value in TRIP_TYPE_CHOICES.iteritems()],
attrs = {'data-name':'trip_type'} ),
required = False)
else:
self.fields[field] = forms.CharField(required = False, widget = forms.TextInput(attrs = {'data-name':field} ))
if request is None:
return FieldEditorForm(instance = report_instance)
else:
return FieldEditorForm(request, instance = report_instance)
And in view
it goes like that:
if request.method == 'POST':
form = createFieldEditorForm(activeaccount, field_id, ride_id, request.POST)
if form.is_valid():
form.save()
messages.success(request, _("New field value successfully added/updated"))
else:
messages.error(request, _("Value was not saved"))
return feedback_to_json(request, form)
and the outcome is - i get success message in browser, but the field is not updated.
i tried overwriting form.save() method and checking if self.cleaned_data contains [field] - and it does. Its right there. Even if i print out form before save and after save i can see, that the data has reached backend and its all nice and neat.. but the damn value is just not saved/updated into database

The save functionality of a ModelForm only works on the fields actually in the fields attribute. Since you're dynamically adding fields, you'll need to save those manually (save the form with commit=False and then add the data to the unsaved object that's returned).

Related

"user_id" is empty when setting MyForm(data=data) (Django)

I have the following model
#models.py
class MyModel(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete = models.CASCADE,null=True)
link = models.URLField(max_length=2048)
date_added = models.DateTimeField(default = timezone.now)
used = models.BooleanField(default = True)
and my form
class MyModelForm(forms.ModelForm):
class Meta:
model = Wishlist
exclude = [
"user","date_added"
]
when I in my view try to "manually" create an instance (in the for-loop) and save it, the "user_id" in my database is NULL
#views.py
def AddLink(request):
user = request.user
instance = MyModel(user=user)
if request.method == "POST":
form = MyModelForm(request.POST,instance = instance)
if form.is_valid():
link = form.instance.wishlist
used = form.instance.discount_pct
#some util function to do some stuff
res = my_util_func(link,api_key)
for used, link in zip(res[0],res[1]):
data = {"link":link,"used":used}
form = MyModelForm(data=data)
form.save()
context = {
"results":res}
return render(request, "discounttracker/myhtml.html",context=context)
I have tried changing data to data = {"link":link,"used":used,"user":user} and data = {"link":link,"used":used,"user_id":user} but it is still empty.
How can I add the user here?
There is a secret option in ModelForm.save() called commit that allows you to not save the form right now, but instead return a model and insert some data into it. This should work perfectly here
form = MyModelForm(data=data))
instance = form.save(commit=False)
instance.user = request.user
instance.save()
This sample inserts the currently logged in user into the user field on your model.

Save two model (One contains foreign key) data from a single template

I'm pretty new to django and working on a blog based project where user can add ratings to specific type of review post.For example giving stars are enabled for restaurant but not for tech stores.
I have created two different form for "review" and "star" model. I want to rate the restaurant using the model named "star" and do it in same template.But I'm having difficulties to do that.
Im getting this error.
"The above exception (NOT NULL constraint failed: reviews_star.post_id_id) was the direct cause of the following exception:"
How do I get the review id that I just save with "review_form.save()".
my review model kinda looks like this (Removed other attributes which aren't related to this problem):
class Review(models.Model):
review_title = models.CharField(verbose_name='Title', max_length=100)
review_body = models.TextField()
author = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE)
restaurant_or_techstore = models.CharField(verbose_name='Foods or Travel',max_length=20)
slug = models.SlugField(null=False,unique=True,max_length = 300)
My rating model looks like this:
class Star(models.Model):
post_id = models.ForeignKey(Review, on_delete = models.CASCADE )
food = models.FloatField(verbose_name='Food',null=False)
service = models.FloatField(verbose_name='Food',null=False)
cleanliness = models.FloatField(verbose_name='Food',null=False)
and my view :
def CreateReview(request):
ImageFormSet = modelformset_factory(Image,form=ImageForm,extra=5)
if request.method == 'POST':
reviewForm = ReviewForm(request.POST)
formset = ImageFormSet(request.POST,request.FILES,queryset=Image.objects.none())
starsForm = StarsrForm(request.POST)
if reviewForm.is_valid() and formset.is_valid() and starsForm.is_valid():
review_form = reviewForm.save(commit=False)
review_form.author = request.user
review_form.post_or_discussion = 1
review_form.food_or_travel = 'Foods'
review_form.save()
reviewForm.save_m2m()
starsForm.save()
for form in formset.cleaned_data:
if form:
image = form['image']
photo = Image(review=review_form,image=image)
photo.save()
messages.success(request,'Image Uploaded Successfully')
return HttpResponseRedirect("/")
else:
print(reviewForm.errors, formset.errors)
else:
reviewForm = ReviewForm()
starsForm = StarsrForm()
formset = ImageFormSet(queryset=Image.objects.none())
return render(request,'reviews/review_form.html',{'reviewForm':reviewForm,'formset':formset,'starsForm':starsForm})
best try when you use ForeighKey is to use related_name attr for this field
class Star(models.Model):
post = models.ForeignKey(Review, related_name='post_review', on_delete = models.CASCADE )
after it you can refer to star from review by some_object_review.post_review.some_field_star
For you error above try reviews_star.post_id.id
Solve The problem.All I had to do is pass the review_form instance to the star form.
new view is:
if request.method == 'POST':
reviewForm = ReviewForm(request.POST)
formset = ImageFormSet(request.POST,request.FILES,queryset=Image.objects.none())
starsForm = StarsrForm(request.POST)
if reviewForm.is_valid() and formset.is_valid() and starsForm.is_valid():
review_form = reviewForm.save(commit=False)
review_form.author = request.user
review_form.post_or_discussion = 1
review_form.food_or_travel = 'Foods'
review_form.save()
reviewForm.save_m2m()
instance = review_form
print(instance.id)
star_form = starsForm.save(commit=False)
star_form.post_id = instance
star_form.save()
for form in formset.cleaned_data:
if form:
image = form['image']
photo = Image(review=review_form,image=image)
photo.save()
messages.success(request,'Image Uploaded Successfully')
return HttpResponseRedirect("/")
else:
print(reviewForm.errors, formset.errors)
else:
reviewForm = ReviewForm()
starsForm = StarsrForm()
formset = ImageFormSet(queryset=Image.objects.none())
return render(request,'reviews/review_form.html',{'reviewForm':reviewForm,'formset':formset,'starsForm':starsForm})

DateField -- "enter a valid date" message

I'm a bit confused as why it keeps returning the 'enter a valid date' message. Is it my formatting?
I've tried different combinations of %m-%d-%Y, but still no luck.
models.py
class DeliveryDate(models.Model):
cart = models.ForeignKey('Cart', null=True, blank=True)
date = models.DateField()
def __str__(self):
return str(self.cart.id)
return self.date
views.py
def add_delivery_date(request):
the_id = request.session['cart_id']
cart = Cart.objects.get(id=the_id)
form = DeliveryDateForm(request.POST or None)
if request.method == "POST":
if form.is_valid():
delivery_date = form.save(commit=False)
date = request.POST['date']
delivery_date = DeliveryDate.objects.create(cart=cart, date=date)
delivery_date.save()
return HttpResponseRedirect('thank-you.html')
context = {
"form": form
}
return render(request, 'choose_delivery_date.html', context)
forms.py
class DeliveryDateForm(forms.ModelForm):
date = forms.DateField(input_formats=['%m %d %Y'], widget=SelectDateWidget, initial=datetime.date.today())
class Meta:
model = DeliveryDate
fields = ['date']
First in your Django ModelForm let's change the date model field's widget without adding an extra field.
This is done in the __ init __ method :
from django.forms.extras.widgets import SelectDateWidget
class DeliveryDateForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(DeliveryDateForm, self).__init__(*args, **kwargs)
#Change date field's widget here
self.fields['date'].widget = SelectDateWidget()
class Meta:
model = DeliveryDate
fields = ['date']
By adding a field like you did :
class DeliveryDateForm(forms.ModelForm):
date = forms.DateField(input_formats=['%m %d %Y'], widget=SelectDateWidget, initial=datetime.date.today())
Is actually adding an extra FormField to your ModelForm which has the same name than the ModelField.
Doing this way, you'll simply change the binded date ModelField widget to 3 selects for Day/month/year
Second, your form validation is quit odd :
What I usually write looks like that:
def add_delivery_date(request):
the_id = request.session['cart_id']
cart = Cart.objects.get(id=the_id)
form = DeliveryDateForm()
if request.method == "POST":
form = DeliveryDate(data=request.POST)
if form.is_valid():
delivery_date = form.save(commit=False)
delivery_date.cart = cart
delivery_date.save()
return HttpResponseRedirect('thank-you.html')
return render(request, 'choose_delivery_date.html', {
'form': form
})

Django formset validating all forms

I have made a formset like this.
class MixedObsCollsForm(forms.Form):
def __init__(self, *args, **kwargs):
project_id = kwargs.pop('project_id')
super(MixedObsCollsForm, self).__init__(*args, **kwargs)
project = get_object_or_404(Project, pk=project_id)
self.fields['photos_upload'] = forms.ImageField(label="Photos", required=False)
taxon = forms.CharField(max_length=80, required=False)
growth_form = forms.ChoiceField(choices = growthFormChoice, required=False)
height = forms.DecimalField(max_value=99, min_value=0, decimal_places=2, required=True)
density = forms.IntegerField(max_value=100, min_value=1, required=False)
And I have a weird way to display it in a view using a class to handle all the processing. But its like this...
class ObservationFormView(object):
def __init__(self, request=None, project_id=None, observation_id=None):
self.request = request
self.ObservationFormSet = formset_factory(form=MixedObsCollsForm, extra=5)
self.ObservationFormSet.form = staticmethod(curry(MixedObsCollsForm, project_id=project_id))
self.project = get_object_or_404(Project, pk=project_id)
self.user = request.user
self.use_type = 'create'
self.formset = self.ObservationFormSet(self.request.POST or None)
def isValid(self):
return self.formset.is_valid() & self.locationForm.is_valid()
def render(self):
return render(self.request, 'observation_form.html', {'use_type': self.use_type, 'formset': self.formset, 'locationForm':self.locationForm, 'project_photos': self.project.photos.all(),})
def processForm(self):
for form in self.formset:
if form.is_valid() == True:
if form.cleaned_data['is_collection'] == True:
collection = self.getOrMakeCollection(form.cleaned_data, self.locationForm.cleaned_data['location'], False)
if form.cleaned_data['is_collection'] == False:
collection = self.getOrMakeCollection(form.cleaned_data, self.locationForm.cleaned_data['location'], True)
observation = self.saveObservation(form.cleaned_data, self.locationForm.cleaned_data['location'], collection)
return observation
The problem is when I render this form in a view even the forms within the formset which are empty don't validate. Reporting "This field is required." next to all the height fields. Even the empty ones.
It was my understanding that the empty forms should always pass validation. I have looked at the management form data and it all looks fine.
If I change required to False it means I end up with IntegrityErrors when I try to save the formset to the database.
I finally worked out what it was so here it is for anyone else who has this problem, its easy to overlook.
It wasn't even included in the code in the question so there wasn't much help anyone could offer.
This line:
growth_form = forms.ChoiceField(choices = growthFormChoice, required=False)
Used the growthFormChoice variable which was a tuple of choices like this.
growthFormChoice = (('0', 'Select one'),
('T', 'tree'),
('S', 'shrub'))
Because of this the default value ('0':'Select One') resulted in passing a 0 to every form which wasn't (intentionally) altered and triggering validation for the rest of the fields. Simply changing it to ('':'Select One') was all it needed.

django formset initial data showing id (primary key)

For some reason, the exclude in my forms isn't working and the primary key of my Item models is showing up on my formset. How can I get rid of it?
Form:
class ItemForm(forms.ModelForm):
class Meta:
model = Item
fields = ('name',
'description',
'quantity',
'start',
'end',
'cost_price',
'selling_price',)
widgets = {
'cost_price': forms.TextInput(attrs={'onChange':'updateSellingPrice()'}),
'description': forms.Textarea,
'start': SelectDateWidget,
'end': SelectDateWidget}
exclude = ('id')
ItemFormSet = modelformset_factory(Item, form=ItemForm, max_num=5, extra=3, exclude=('id'))
View:
def item_details(request, event_slug, role_id, module_slug):
event = get_object_or_404(Event, slug=event_slug)
payment_details = EventPaymentDetail.objects.get_or_create(event=event)[0]
try:
item_details = Item.objects.filter(event=event)
except:
item_details = Item.objects.get_or_create(event=event)[0]
if request.method == 'POST':
item_formset = ItemFormSet(request.POST)
#display_error(request, item_formset)
if item_formset.is_valid():
instances = item_formset.save(commit=False)
for instance in instances:
instance.event = event
instance.save()
messages.success(request, 'Item details successfully saved!')
url = reverse('event_admin_dashboard', args=[event_slug, role_id])
return redirect(url)
else:
item_formset = ItemFormSet()
currency_type = payment_details.currency
template = 'registration/item_details.html'
return render(request, template, locals())
I don't believe it is possible to exclude the id field. I'm afraid I can't give you an explanation or a link to the docs.
Aside:
It's not the issue here, but you're missing a comma on your exclude tuple. That's not the problem here, but it means that django interprets it as
exclude = ('i', 'd')
It should be:
exclude = ('id',)