Django ModelForm Custom Date Field - django

I am trying to create a reservation form that creates an object for model Reservation when the form is posted. I am using a custom datepicker widget to pick the ate, but I am also using ModelForms.
The issue is that, if I do not have 'date' listed in the meta fields list in the forms.py, then the form doesn't look for the date field form input on post. But if I include 'date' inside the meta fields of the forms.py Modelform, then it errors and says "date field can not be left blank" even though it is not blank...
forms.py
class ReservationForm(forms.ModelForm):
date = forms.DateField(
widget=DatePickerInput(format='%m/%d/%Y')
)
def clean_date(self):
data = self.cleaned_data['date']
# Check if a date is not in the past.
if data < datetime.date.today():
raise ValidationError(_('Invalid date - reservation in past'), code='invalid')
messages.danger(request, "Reservation Created")
print('ERROR')
# Remember to always return the cleaned date.
return data
class Meta:
model = Reservation
fields = ('reservation_time', 'people', 'name', 'email', 'phone') # REMOVED 'date'
views.py
def reservationFormView(request):
#reservation = create(Reservation)
# If this is a POST request then process the Form data
if request.method == 'POST':
# Create a form instance and populate it with data from the request (binding):
form = ReservationForm(request.POST)
# Check if the form is valid:
if form.is_valid():
# process the data in form.cleaned_data as required
reservation = form.save(commit=False)
reservation.ReservationEmail = form.cleaned_data['email']
reservation.ReservationName = form.cleaned_data['name']
reservation.ReservationPeople = form.cleaned_data['people']
reservation.ReservationTime = form.cleaned_data['reservation_time']
reservation.date = form.cleaned_data['date']
print( reservation.date)
#reservation.created_time = timezone.now()
reservation.save()
# redirect to a new URL:
return HttpResponseRedirect('/reservation-confirmation/')
# If this is a GET (or any other method) create the default form.
else:
form = ReservationForm()
return render(request, 'home/reservation_form.html', {'form': form, })
models.py
class Reservation(BaseModel):
class Meta:
verbose_name_plural = "Reservations"
TIME_CHOICES = (
...
)
SEATING_CHOICES = (
...
)
date = models.DateField(null=True)
name = models.CharField(max_length=35, null=True)
phone = PhoneNumberField(null=True) #USE THIS https://github.com/stefanfoulis/django-phonenumber-field
email = models.EmailField(null=True)
people = models.PositiveSmallIntegerField(choices=SEATING_CHOICES, default=None, db_index=True)
reservation_time = models.PositiveSmallIntegerField(choices=TIME_CHOICES, default=None, db_index=True)
def __str__(self):
return '(%s) %s %s' % (self.date, self.name, self.phone )

Your clean_date method does not return a value in case the if condition is False. You should return the cleaned data in case it is correct, like:
def clean_date(self):
data = self.cleaned_data['date']
# Check if a date is not in the past.
if data < datetime.date.today():
raise ValidationError(_('Invalid date - reservation in past'), code='invalid')
messages.danger(request, "Reservation Created")
# not indented under the if
return data
Otherwise, this function will return None in case the data is valid, and raise a ValidationError in case the data is invalid.

Related

Replacing the value of one form field with another

I have written a piece of code where the user can select an occupation from a select box (teacher, doctor, pilot, etc) and if their occupation isn't in the list they can select 'other' then write their occupation in a textbox underneath.
I can successfully detect if they selected 'other' from the dropdown box, but cannot figure out how to populate the 'occupation' field with the data from the 'other' field.
if request.method == 'POST':
form = OccupationForm(request.POST, request.FILES, instance=request.user.occupation)
if form.is_valid():
# if user selected 'other' - get input from text field
if form['occupation'].value() == 'other':
# this doesnt work
#form_data = self.get_form_step_data(form)
#form.other = form_data.get('other', '')
#form.save()
return redirect('#')
#form.save()
#return redirect('#')
else:
form = OccupationForm(instance=request.user.occupation)
Thank you.
EDIT:
shortened models.py
class Occupation(models.Model):
# I just realized, maybe this should be OneToManyField ??
user = models.OneToOneField(User, on_delete=models.CASCADE)
OCCUPATIONS = (
('teacher', 'Teacher'),
('doctor', 'Doctor'),
('other', 'Other'),
)
occupation = models.CharField('What is your job?', max_length=200, null=True, choices=OCCUPATIONS)
shortened forms.py
class OccupationsForm(forms.ModelForm):
other = forms.CharField(required=False, label='')
def clean(self):
cleaned_data = super().clean()
if cleaned_data.get('occupations') == 'other':
cleaned_data['occupations'] = cleaned_data.get('other')
return cleaned_data
class Meta:
model = Occupations
fields = ['occupations']
Thank you
The clean methods of your form is the place where you validate and clean data sent to the form, this includes potentially changing the data.
class OccupationForm(forms.ModelForm):
....
def clean(self):
cleaned_data = super().clean()
if cleaned_data.get('occupation') == 'other':
cleaned_data['occupation'] = cleaned_data.get('other')
return cleaned_data
Now you can just call form.save straight after form.is_valid as the clean method will have cleaned/returned the correct data

Django modelforms, foreignkey filter for data owned by logged in user [duplicate]

I'm trying to display a form (ModelForm) with a select field filtered by currently logged in user. The select field in this case contains a list of categories. I want to display only the categories which "belong" to the currently logged in user. The category field is a foreign key to the IngredienceCategory model.
Here is what I've come up with so far but it's giving me an error (unexpected keyword queryset). Any ideas what I'm doing wrong?
# models.py
class IngredienceCategory(models.Model):
name = models.CharField(max_length=30)
user = models.ForeignKey(User, null=True, blank=True)
class Meta:
verbose_name_plural = "Ingredience Categories"
def __unicode__(self):
return self.name
class Ingredience(models.Model):
name = models.CharField(max_length=30)
user = models.ForeignKey(User, null=True, blank=True)
category = models.ForeignKey(IngredienceCategory, null=True, blank=True)
class Meta:
verbose_name_plural = "Ingredients"
def __unicode__(self):
return self.name
class IngredienceForm(ModelForm):
class Meta:
model = Ingredience
fields = ('name', 'category')
# views.py
def home(request):
if request.user.is_authenticated():
username = request.user.username
email = request.user.email
foods = Food.objects.filter(user=request.user).order_by('name')
ingredients = Ingredience.objects.filter(user=request.user).order_by('name')
ingrcat = IngredienceCategory.objects.filter(user=request.user)
if request.method == 'POST':
form = IngredienceForm(request.POST)
if form.is_valid():
# Create an instance of Ingredience without saving to the database
ingredience = form.save(commit=False)
ingredience.user = request.user
ingredience.save()
else:
# How to display form with 'category' select list filtered by current user?
form = IngredienceForm(queryset=IngredienceCategory.objects.filter(user=request.user))
context = {}
for i in ingredients:
context[i.category.name.lower()] = context.get(i.category.name.lower(), []) + [i]
context2 = {'username': username, 'email': email, 'foods': foods, 'ingrcat': ingrcat, 'form': form,}
context = dict(context.items() + context2.items())
else:
context = {}
return render_to_response('home.html', context, context_instance=RequestContext(request))
That's happening because ModelForm does not take a queryset keyword.
You can probably achieve this by setting the queryset on the view:
form = IngredienceForm()
form.fields["category"].queryset =
IngredienceCategory.objects.filter(user=request.user)
See related question here.
Here i have another suggestion to solve the problem. You can pass request object in your form object inside view.
In view.py just pass the request object.
form = IngredienceForm(request)
In your forms.py __init__ function also add request object
from models import IngredienceCategory as IC
class IngredienceForm(ModelForm):
class Meta:
model = Ingredience
fields = ('name', 'category')
def __init__(self, request, *args, **kwargs):
super(IngredienceForm, self).__init__(*args, **kwargs)
self.fields['name'].queryset = IC.objects.filter(user=request.user)
This filter always will be applied whenever you initialize your form .

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
})

Filter select field in ModelForm by currently logged in user

I'm trying to display a form (ModelForm) with a select field filtered by currently logged in user. The select field in this case contains a list of categories. I want to display only the categories which "belong" to the currently logged in user. The category field is a foreign key to the IngredienceCategory model.
Here is what I've come up with so far but it's giving me an error (unexpected keyword queryset). Any ideas what I'm doing wrong?
# models.py
class IngredienceCategory(models.Model):
name = models.CharField(max_length=30)
user = models.ForeignKey(User, null=True, blank=True)
class Meta:
verbose_name_plural = "Ingredience Categories"
def __unicode__(self):
return self.name
class Ingredience(models.Model):
name = models.CharField(max_length=30)
user = models.ForeignKey(User, null=True, blank=True)
category = models.ForeignKey(IngredienceCategory, null=True, blank=True)
class Meta:
verbose_name_plural = "Ingredients"
def __unicode__(self):
return self.name
class IngredienceForm(ModelForm):
class Meta:
model = Ingredience
fields = ('name', 'category')
# views.py
def home(request):
if request.user.is_authenticated():
username = request.user.username
email = request.user.email
foods = Food.objects.filter(user=request.user).order_by('name')
ingredients = Ingredience.objects.filter(user=request.user).order_by('name')
ingrcat = IngredienceCategory.objects.filter(user=request.user)
if request.method == 'POST':
form = IngredienceForm(request.POST)
if form.is_valid():
# Create an instance of Ingredience without saving to the database
ingredience = form.save(commit=False)
ingredience.user = request.user
ingredience.save()
else:
# How to display form with 'category' select list filtered by current user?
form = IngredienceForm(queryset=IngredienceCategory.objects.filter(user=request.user))
context = {}
for i in ingredients:
context[i.category.name.lower()] = context.get(i.category.name.lower(), []) + [i]
context2 = {'username': username, 'email': email, 'foods': foods, 'ingrcat': ingrcat, 'form': form,}
context = dict(context.items() + context2.items())
else:
context = {}
return render_to_response('home.html', context, context_instance=RequestContext(request))
That's happening because ModelForm does not take a queryset keyword.
You can probably achieve this by setting the queryset on the view:
form = IngredienceForm()
form.fields["category"].queryset =
IngredienceCategory.objects.filter(user=request.user)
See related question here.
Here i have another suggestion to solve the problem. You can pass request object in your form object inside view.
In view.py just pass the request object.
form = IngredienceForm(request)
In your forms.py __init__ function also add request object
from models import IngredienceCategory as IC
class IngredienceForm(ModelForm):
class Meta:
model = Ingredience
fields = ('name', 'category')
def __init__(self, request, *args, **kwargs):
super(IngredienceForm, self).__init__(*args, **kwargs)
self.fields['name'].queryset = IC.objects.filter(user=request.user)
This filter always will be applied whenever you initialize your form .

Django IntegrityError signup_simplesubscriber.date_created may not be NULL

I've read every "InterityError" + "may no be NULL" post and still can't track down what's causing this error.
I've got a two-part signup form. First part is just selecting a product. That passes a product ID to the next page as part of the URL, where they input personal info. I can get the form to work fine until I start removing fields -- i'm using model forms -- because some fields don't need to be displayed.
Here's my model, and the modelForm:
class SimpleSubscriber(models.Model):
name = models.CharField(max_length=255)
address = models.CharField(max_length=200)
city = models.CharField(max_length=100)
state = models.CharField(max_length=2)
zipcode = models.CharField(max_length=9)
phone = models.CharField(max_length=10)
email = models.EmailField()
date_created = models.DateTimeField(null=True)
sub_type = models.ForeignKey(Product)
def __unicode__(self):
return self.name
class SubscriberForm(ModelForm):
class Meta:
model = SimpleSubscriber
fields = ('name', 'address', 'city', 'state', 'zipcode', 'phone', 'email', 'sub_type',)#'date_created',
And here's my views:
def select_product(request):
title = "get yourself an e-edition. wurd."
pform = Product.objects.order_by('product_active')
if request.method == 'POST': # If the form has been submitted...
pform = ProductForm(request.POST) # A form bound to the POST data
if pform.is_valid(): # All validation rules pass
# ...
return HttpResponseRedirect('signup/%i' % pform.id) # Redirect after POST
else:
form = ProductForm() # An unbound form
return render_to_response('signup/index.html', {'title': title, 'pform': pform}, context_instance=RequestContext(request))
def subscriber_signup(request, product_id):
productchoice = Product.objects.get(id=product_id)
now = datetime.datetime.now()
title = "We need some information."
if request.method == 'POST': # If the form has been submitted...
sform = SubscriberForm(request.POST) # A form bound to the POST data
if sform.is_valid(): # All validation rules pass
sform.date_created = now
sform.sub_type = productchoice
sform.save()
return HttpResponseRedirect('thankyou/') # Redirect after POST
else:
sform = SubscriberForm() # An unbound form
return render_to_response('signup/detail.html', {'title': title, 'sform': sform, 'productchoice': productchoice, 'now': now.date(),}, context_instance=RequestContext(request))
I think it has something to do with the modelForm, but I'm pretty new, so I really have no idea. If I add all the fields to SubscriberForm, then they get filled out and everything works fine. But I don't want users to have to say when they filled out the form, so i put sform.date_created = now and I want the product_id to be filled in automatically by what choice they picked on the previous page. but if I exclude these fields from the form it throws the IntegrityError, which isn't very helpful in explaining what to change.
Any hints on where I'm messing up?
Thanks,
Two things:
1) You may benefit from using exlude in your form definition:
class SubscriberForm(ModelForm):
class Meta:
model = SimpleSubscriber
exclude = ('date_created', )
2) To your question, heres how to fix it:
if sform.is_valid(): # All validation rules pass
suscriber = sform.save(commit=False)
suscriber.date_created = now
suscriber.sub_type = productchoice
suscriber.save()
Alternatively to #fceruti's suggestion, you can also add more kwarg tags null=True on the model's field where appropriate - only forcing a minimal set of fields to be completed in the form.