Anyone who can explain me, why my ValidationError in my form doesn't work? I can see "Test" in my terminal, but the ValidationError doesn't show. I am happy to share the full code, but maybe the mistake already lies in the snippet below?
def clean_ticket(self):
ticket = self.cleaned_data['ticket']
if not self.event == ticket.event:
print("Test")
raise forms.ValidationError(
_("The ticket you chose doesn't belong to your event."),
code='ticket_belongs_to_event_validation',
)
return ticket
Full length code:
class ReserveForm(forms.ModelForm):
"""
* Pass inital tickets into form.
* Check if quantity is lower than defined max qty per ticket.
* Check if quantity is available.
* Clean function for quantity field.
"""
class Meta:
model = ReservedItem
fields = ['ticket', 'quantity']
def __init__(self, organizer, event, *args, **kwargs):
self.organizer = organizer
self.event = event
self.ticket = kwargs['initial']['ticket']
super().__init__(*args, **kwargs)
self.fields['ticket'].widget = forms.HiddenInput()
self.fields['quantity'].widget.attrs['placeholder'] = '0'
self.fields['quantity'].widget.attrs['class'] = 'form-control' # TODO Marc
def check_if_qty_lower_than_max_qty_per_ticket(self, new_added_quantity, max_qty_per_ticket):
if new_added_quantity and new_added_quantity > max_qty_per_ticket:
raise forms.ValidationError(
_("You can only buy %(max_quantity)s tickets per ticket type."),
code='max_qty_per_ticket',
params={'max_quantity': max_qty_per_ticket},
)
def check_if_qty_is_available(self, new_added_quantity):
ticket_pk = self.ticket.pk
ticket_name = self.ticket.name
max_quantity = self.ticket.quantity
# Check OrderItem
order_items_qty = OrderItem.objects.filter(
ticket__pk=ticket_pk
).aggregate(Sum('quantity'))
# Check ReservedItem within the last x minutes
time_threshold = timezone.now() - timedelta(minutes=settings.MINUTES_TICKET_RESERVATION)
reserved_items_qty = ReservedItem.objects.filter(
ticket__pk=ticket_pk,
created__gt=time_threshold,
).aggregate(Sum('quantity'))
# Calculate reserved quantity and new quantity
order_items_qty = order_items_qty['quantity__sum']
reserved_items_qty = reserved_items_qty['quantity__sum']
order_items_qty = 0 if not order_items_qty else order_items_qty
reserved_items_qty = 0 if not reserved_items_qty else reserved_items_qty
qty_reserved = order_items_qty + reserved_items_qty
new_quantity = qty_reserved + new_added_quantity
# Validation
if new_quantity > max_quantity:
tickets_left_total = max_quantity - qty_reserved
raise forms.ValidationError(
_("%(ticket_name)s: There are not enough tickets left. Maximum you can choose is %(value)s."),
code='qty_available',
params={'ticket_name': ticket_name, 'value': tickets_left_total},
)
def clean_ticket(self):
ticket = self.cleaned_data['ticket']
if not self.event == ticket.event:
print("Test")
raise forms.ValidationError(
_("The ticket you chose doesn't belong to your event."),
code='ticket_belongs_to_event_validation',
)
return ticket
def clean_quantity(self):
new_added_quantity = self.cleaned_data['quantity']
if new_added_quantity:
self.check_if_qty_lower_than_max_qty_per_ticket(
new_added_quantity=new_added_quantity,
max_qty_per_ticket=self.organizer.max_qty_per_ticket,
)
self.check_if_qty_is_available(
new_added_quantity=new_added_quantity,
)
# Always return a value to use as the new cleaned data, even if
# this method didn't change it.
return new_added_quantity
Related
I try to filter items in a admin list:
class Product(models.Model):
name = models.CharField("name", max_length = 128)
def associated_stores(self):
stores = set([f"""{s.number}""" for sin self.store_name.all()])
if len(stores) == 0:
return None
return ", ".join(stores)
class Store(models.Model):
products = models.ManyToManyField(Product, related_name = "%(class)s_name", related_query_name = "product_store_qs", blank = True)
number = ...
Now I want to implement a SimpleListFilter that can filter in the products list for products that are available in no stores, or show the store names:
class AssociatedStoresFilter(admin.SimpleListFilter):
title = "associated stores"
parameter_name = "assoc_stores"
def lookups(self, request, model_admin):
return [(True, "yes"), (False, "no")]
def queryset(self, request, queryset):
qs = queryset.annotate(assoc_stores = Case(When(product_store_qs__isnull = False, then = 1), default = 0))
if self.value() == "False":
return qs.filter(assoc_stores = 0)
elif self.value() == "True":
return qs.filter(assoc_stores = 1)
else:
return queryset
The filter for no stores have the product works, but the one where only products should be shown that are available in a store shows one entry for every store of a product (with all stores) instead of only one entry (with all stores) only.
How do I cut down my results to hold every product only once after filtering?
edit: I know I can solve my problem using Count (but I want to understand why the other code does not work, or how to do it smarter):
def queryset(self, request, queryset):
qs = queryset.annotate(assoc_stores = Count("product_store_qs"))
if self.value() == "False":
return qs.filter(assoc_stores = 0)
elif self.value() == "True":
return qs.filter(assoc_stores__gte 1)
else:
return queryset
So i've been stack on this problem that everytime I update my stock in orders it called twice for updating I use the class base view built in Django but in models I have a post signal which it removing the stock of the product. I already use the dispatch_uid but it's not working or any solution that I found on internet not working on my problem.
here is my class base view for updating:
class ItemQtyUpdateClassBaseView(UpdateView):
template_name = 'components/update/item.html'
form_class = ItemForm
def get_context_data(self, **kwargs):
kwargs['context_title'] = 'Update Order Qty'
return super(ItemQtyUpdateClassBaseView, self).get_context_data(**kwargs)
def form_valid(self, form):
try:
order_item = OrderModel.objects.get(pk = self.kwargs[self.pk_url_kwarg])
fetch_product = ProductsModel.objects.get(product_name = order_item.product_name)
print(fetch_product.qty)
if fetch_product.dough != None:
if int(fetch_product.dough.qty) <= 0:
messages.error(self.request, 'Not enough qty to order.')
return self.form_invalid(form)
if int(fetch_product.qty) <= 0:
messages.error(self.request, 'Not enough qty to order.')
return self.form_invalid(form)
else:
self.object = form.save()
messages.success(self.request, 'Successfully update qty.')
except Exception as e:
messages.error(self.request, e)
return self.form_invalid(form)
return super(ItemQtyUpdateClassBaseView, self).form_valid(form)
def form_invalid(self, form):
return super().form_invalid(form)
def get_queryset(self):
return OrderModel.objects.filter(pk = self.kwargs[self.pk_url_kwarg])
def get_success_url(self):
return reverse_lazy('core:createorder', kwargs = {
'typeoforder': self.kwargs['typeoforder']
})
and here is my model with signal post_save:
class OrderModel(models.Model):
date = models.DateField(auto_now = True)
invoice_no = models.ForeignKey(InvoiceModel, on_delete = models.CASCADE)
product_name = models.CharField(max_length = 150, blank = False, null = True)
price = models.DecimalField(max_digits = 10, decimal_places = 2)
qty = models.FloatField(default = 1)
class Meta:
db_table = 'orders'
ordering = ['-pk']
#receiver(post_save, sender = OrderModel, dispatch_uid='signals.ordermodel_removeqty')
def removeQty(sender, instance = None, created = False, **kwargs):
product = ProductsModel.objects.get(product_name = instance.product_name)
if product.dough != None:
dough = DoughModel.objects.get(pk = product.dough.pk)
dough.qty = abs(dough.qty - (product.qty * instance.qty))
dough.save()
else:
product.qty = abs(product.qty - instance.qty)
product.save()
You are calling save() method two times in form_valid() method :
self.object = form.save()
super(ItemQtyUpdateClassBaseView, self).form_valid(form)
You have to remove the below line from form_valid() method:
self.object = form.save()
See what form_valid(form)(Django Docs) method do:
Saves the form instance, sets the current object for the view, and
redirects to get_success_url().
I have a timeclock app for employees to check-in/check-out with which works ok, however when you're on the user-activity page and either check in or check out once it submits the form it sends you back to the user-activity.html page, but no data is loaded in the table and its not loading data from the view. Once you refresh the page the data is there again but I'm not sure why this is happening. I'll attach 2 images, one before the checkin/checkout button is pressed, and one after
and here is my view. Thanks for any help you can give
class ActivityView(LoginRequiredMixin, View):
def get(self, request, *args, **kwargs):
context = {}
toggle = UserActivity.objects.current(request.user)
user_data = UserActivity.objects.filter(user=request.user).order_by('-timestamp')
current_users = UserActivity.objects.order_by('user', '-timestamp').distinct('user')
context['toggle'] = toggle
context['user_data'] = user_data
context['current_users'] = current_users
return render(request, "administration/timesheets/user-activity.html", context)
def post(self, request, *args, **kwargs):
context = {}
toggle = UserActivity.objects.toggle(request.user)
context['toggle'] = toggle
return render(request, "administration/timesheets/user-activity.html", context)
EDIT
Also here is my model...
USER_ACTIVITY_CHOICES = (
('checkin', 'Check In'),
('checkout', 'Check Out'),
)
class UserActivityManager(models.Manager):
def current(self, user):
current_obj = self.get_queryset().filter(user=user).order_by('-timestamp').first()
return current_obj
def toggle(self, user):
last_item = self.current(user)
activity = "checkin"
if last_item is not None:
if last_item.timestamp <= tz.localize(datetime.datetime.now()):
pass
if last_item.activity == "checkin":
activity = "checkout"
obj = self.model(
user=user,
activity=activity
)
obj.save()
return obj
class UserActivity(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL)
activity = models.CharField(max_length=120, default='checkin', choices=USER_ACTIVITY_CHOICES)
timestamp = models.DateTimeField(auto_now_add=True)
time_delta = models.DecimalField(decimal_places=2, max_digits=4, default='0.00', blank=True, null=True)
status = models.CharField(max_length=60, null=True, blank=True)
description = models.TextField(blank=True, null=True)
objects = UserActivityManager()
def __unicode__(self):
return str(self.activity)
def __str__(self):
return str(self.activity)
class Meta:
verbose_name = 'User Activity'
verbose_name_plural = "User Activities"
def next_activity(self):
next = "Check in"
if self.activity == "checkin":
next = "Check out"
return next
#property
def current(self):
current = 'Checked Out'
if self.activity == 'checkin':
current = "Checked in"
return current
def clean(self, *args, **kwargs):
if self.user:
user_activities = UserActivity.objects.exclude(
id=self.id
).filter(
user = self.user
).order_by('-timestamp')
if user_activities.exists():
recent_ = user_activities.first()
if self.activity == recent_.activity:
message = "%s is not a valid activity for this user" %(self.get_activity_display())
raise ValidationError(message)
else:
if self.activity != "checkin":
message = "%s is not a valid activity for this user" %(self.get_activity_display())
raise ValidationError(message)
return super(UserActivity, self).clean(*args, **kwargs)
In your post function, I think you have a typing mistake :
toggle = UserActivity.objects.toggle(request.user)
I think that instead, you need :
toggle = UserActivity.objects.current(request.user)
Any help appreciated.
I cannot save parent Program object in admin.py. It only does not work for 1 particular model with inlines, and only with one particular inline. If I take ProgramHotelInline out, I can save the model. If ProgramHotelInline is in inlines for ProgramAdmin, I can't save the model; the terminal output gives: "POST /admin_keywords_submit/ HTTP/1.1" 403 2294
Here is the PageAdmin class:
class ProgramHotelInline(admin.StackedInline):
model = ProgramHotel
extra = 5
def get_formset(self, request, obj=None, **kwargs):
initial = []
formset = super(ProgramHotelInline, self).get_formset(request, obj, **kwargs)
#initial c/i-c/o
if obj:
if request.method == "GET":
for bh in range(0,self.extra):
initial+=[{'check_in':datetime.combine(obj.base_arrival,time(14,00)),
'check_out':datetime.combine(obj.base_departure,time(12,00))}]
self.extra = len(initial)
formset.__init__ = curry(formset.__init__, initial=initial)
return formset
def queryset(self, request):
"""
Prefetch info.
"""
qs = super(ProgramHotelInline, self).queryset(request)
return qs.select_related('hotel__organization','roomtype')
program_extra_fieldsets = ((None, {"fields": ("base_arrival","base_departure","program_type","default_commission","content","no_bed_age","free_child_age","short_description","images")}),)
class ProgramAdmin(PageAdmin):
inlines = (CodeInline,ProgramExcursionInline,ProgramTransferInline, ProgramHotelInline,ProgramCommissionInline,ProgramDiscountInline)
fieldsets = deepcopy(PageAdmin.fieldsets) + program_extra_fieldsets
filter_horizontal = ('images', )
admin.site.register(Program,ProgramAdmin)
here are the models:
class ProgramHotel(models.Model):
program = models.ForeignKey(Program,null=True,blank=True)
hotel = models.ForeignKey('tour.Hotel')
roomtype = ChainedForeignKey(
'tour.RoomType',
chained_field="hotel",
chained_model_field="hotel",
show_all=False,
auto_choose=False,blank=True,null=True,help_text="<span style='color:blue'>null means no bed</span>")#null stands for (child) with no bed!
check_in = models.DateTimeField(default=default_ci_time)
check_out = models.DateTimeField(default=default_co_time)
#max_age = models.IntegerField(default=0)
pax_per_room = models.SmallIntegerField()
exb = models.BooleanField(default=False)
bb = models.BooleanField(default=False)
turn = models.SmallIntegerField(default = 1)#if we have more than one stopping, first stopping - turn=1,2nd - turn=2 etc
USD = '$'
KRW = unichr(8361)
CURRENCY_CHOICES = (
(USD, 'USD'),
(KRW, 'KRW'),
)
currency = models.CharField(default = "$",max_length=1,choices=CURRENCY_CHOICES)
price = models.DecimalField('price per pax, including comm.',decimal_places=0, max_digits=21, default = 0)#in USD
def __unicode__(self):
if self.roomtype:
if self.pax_per_room == 2:
rt= u"\u00BD%s" % (self.roomtype.roomtype)
elif self.pax_per_room == 3:
rt= u"\u2153%s" % (self.roomtype.roomtype)
elif self.pax_per_room == 4:
rt= u"\u00BC%s" % (self.roomtype.roomtype)
elif self.pax_per_room == 5:
rt= u"\u2155%s" % (self.roomtype.roomtype)
elif self.pax_per_room == 1:
rt= u"%s, single occupancy" % (self.roomtype.roomtype)
elif self.pax_per_room > 5:
rt= u"1/%s %s" % (self.pax_per_room,self.roomtype.roomtype)
else:
rt= u"%s" % (self.roomtype.roomtype)
else:
rt="no bed"
bb = u", breakfast" if self.bb else u", NO BREAKFAST"
exb = u", +extra bed" if self.exb else u""
return u"%s: %s, %s ~ %s%s%s; %s%s" % (self.hotel.organization,rt,self.check_in.strftime("%d %B %Y"), self.check_out.strftime("%d %B %Y"),bb,exb,self.price,self.currency)
class Meta:
unique_together = (("program", "turn","hotel","roomtype","check_in","pax_per_room","exb","bb"),)
class Program(Page,RichText):
def default_commission_default():
l = list(DynamicParameter.within_dates.within_dates(datetime.today()))[:1]
dynamic = l[0]
return dynamic.default_agent_commission
def no_bed_age_default():
l = list(DynamicParameter.within_dates.within_dates(datetime.today()))[:1]
dynamic = l[0]
return dynamic.default_no_bed_age
def free_child_age_default():
l = list(DynamicParameter.within_dates.within_dates(datetime.today()))[:1]
dynamic = l[0]
return dynamic.default_free_child_age
program_type = models.ManyToManyField(ProgramType)
images = models.ManyToManyField(PicasaGallery,null=True, blank=True)
short_description = RichTextField(max_length=250,default = "")
#excursions, transfers, hotels - just a template; actuals are in Code
hotels = models.ManyToManyField('tour.Hotel', through='ProgramHotel')
transfers = models.ManyToManyField('misc.Transfer', through='ProgramTransfer')
excursions = models.ManyToManyField("tour.Excursion", through='ProgramExcursion')
commission = models.ManyToManyField(Customer, through='ProgramCommission')
default_commission = models.DecimalField('agent commission',decimal_places=3, max_digits=4, default = default_commission_default)
base_arrival = models.DateField()
base_departure = models.DateField()
no_bed_age = models.SmallIntegerField(default=no_bed_age_default) #UP TO THIS AGE ALLOWED TO HAVE NO BED
free_child_age = models.SmallIntegerField(default=free_child_age_default) #NO BED+BREAKFAST FREE, IF ADULTS HAVE BREAKFAST; no tickets!
discount = models.ManyToManyField(Discount, through='ProgramDiscount')
def save(self, *args, **kwargs):
''' On save, make invisible in left and foot menu
SET COMMISSION, IF DEFINED
add bonus for a company, if any
'''
self.in_menus = '1'
super(Program, self).save(*args, **kwargs)
if self.default_commission:
for customer in Customer.objects.filter(customer_type="AGENT"):
try:
pc = ProgramCommission.objects.get(program=self,customer=customer)
except ObjectDoesNotExist:
pc = ProgramCommission(program=self,customer=customer, commission=self.default_commission)
#print pc
if not pc.id:
if pc.customer.organization=="Private Customer":
pc.commission = 0.00
try:
customer_comm = CustomerCommission.within_dates.within_dates(self.base_arrival).get(customer=customer).bonus_commission
if customer_comm >0:
pc.commission = pc.commission + customer_comm
except ObjectDoesNotExist:
pass
pc.save()
def __unicode__(self):
return u"%s" % (self.title)
def groupcode(self):
"""
return filtered set - only code.group=True
"""
return self.code_set.filter(group=True,arr_date__gte=datetime.today())
def distincthotel(self):
"""
return filtered set - only distinct hotels
"""
return self.programhotel_set.distinct('hotel')
def get_admin_url(self):
return urlresolvers.reverse("admin:%s_%s_change" %
(self._meta.app_label, self._meta.module_name), args=(self.id,))
class Meta:
ordering = ['title']
Found the source of mistake: that was outdated django-smart-selects. After update it works again.
Newbie to all this! i'm working on displaying phone field displayed as (xxx)xxx-xxxx on front end.below is my code. My question is 1. all fields are mandatory, for some reason,phone is not behaving as expected.Even if it is left blank its not complaining and 2.how can i test this widget's functionality
class USPhoneNumberWidget(forms.MultiWidget):
def __init__(self,attrs=None):
widgets = (forms.TextInput(attrs={'size':'3','maxlength':'3'}),forms.TextInput(attrs={'size':'3','maxlength':'3'}),forms.TextInput(attrs={'size':'3','maxlength':'4'}))
super(USPhoneNumberWidget,self).__init__(widgets,attrs=attrs)
def decompress(self, value):
if value:
val = value.split('-')
return [val[0],val[1],val[2]]
return [None,None,None]
def compress(self, data_list):
if data_list[0] and data_list[1] and data_list[2]:
ph1 = self.check_value(data_list[0])
ph2 = self.check_value(data_list[1])
ph3 = self.check_value(data_list[2])
return '%s''%s''%s' %(ph1,ph2,ph3)
else:
return None
def check_value(self,val):
try:
if val.isdigit():
return val
except:
raise forms.ValidationError('This Field has to be a number!')
def clean(self, value):
try:
value = re.sub('(\(|\)|\s+)','',smart_unicode(value))
m = phone_digits_re.search(value)
if m:
return u'%s%s%s' % (m.group(1),m.group(2),m.group(3))
except:
raise ValidationError('Phone Number is required.')
def value_from_datadict(self,data,files,name):
val_list = [widget.value_from_datadict(data,files,name+'_%s' %i) for i,widget in enumerate(self.widgets)]
try:
return val_list
except ValueError:
return ''
def format_output(self,rendered_widgets):
return '('+rendered_widgets[0]+')'+rendered_widgets[1]+'-'+rendered_widgets[2]
class CustomerForm(ModelForm):
phone = forms.CharField(required=True,widget=USPhoneNumberWidget())
class Meta:
model = Customer
fields = ('fname','lname','address1','address2','city','state','zipcode','phone')
In models blank and null are not true.
Any input it highly appreciated.Thanks
Here is the phone field:
phone = forms.CharField(label = 'Phone',widget=USPhoneNumberWidget()
class USPhoneNumberWidget(forms.MultiWidget):
"""
A widget that splits phone number into areacode/next3/last4 with textinput.
"""
def __init__(self,attrs=None):
widgets = (forms.TextInput(attrs={'size':'3','maxlength':'3'}),forms.TextInput(attrs={'size':'3','maxlength':'3'}),forms.TextInput(attrs={'size':'4','maxlength':'4'}))
super(USPhoneNumberWidget,self).__init__(widgets,attrs=attrs)
def decompress(self, value):
if value:
val = value
return val[:3],val[3:6],val[6:]
return None,None,None
def compress(self, data_list):
if data_list[0] and data_list[1] and data_list[2]:
return '%s''%s''%s' %(data_list[0],data_list[1],data_list[2])
else:
return None
def value_from_datadict(self,data,files,name):
val_list = [widget.value_from_datadict(data,files,name+'_%s' %i) for i,widget in enumerate(self.widgets)]
if val_list:
return '%s''%s''%s' %(val_list[0],val_list[1],val_list[2])
def format_output(self,rendered_widgets):
return '( '+rendered_widgets[0]+' )'+rendered_widgets[1]+' - '+rendered_widgets[2]
But depending on how you store the phone# in db 'return' line is to be changed. here I'm accepting it as (xxx)-xxx-xxxx format.In compress it receives ph_0(areacode),ph_1(next 3),ph_2(last4) in that order.but I'm storing it as xxxxxxxxxx.
Firebug helped me understand better about what return values should be. I'll update the answer when i come to know how testing could be done.