Kerosive Kerosive - 29 days ago 8
Python Question

Form coming back with <django.db.models.query_utils.DeferredAttribute object at 0x10e6ee898>

Disclaimer: I am fairly new to Django (trying to teach myself) and am currently trying to get a package that had little to no documentation https://github.com/byteweaver/django-coupons working for an app I am building.

I am trying to redeem a coupon I have created on the backend and I am getting some very strange errors. When I submit my form the

<django.db.models.query_utils.DeferredAttribute object at 0x10e6ee898>
that comes back in the form box and I am given a "This code is not valid" error. Any assistance on this would be greatly appreciated as I have been beating my head against a wall with this for hours and hours now.

views.py

def gift_card(request):

user = request.user

if request.user.is_authenticated:
edit_profile = EditProfileForm(user=user)
redeem = CouponForm()
if request.method == 'POST':
edit_profile = EditProfileForm(request.POST, user=user)
redeem = CouponForm(request.POST, user=user)
if redeem.is_valid():
Coupon.redeem(request.POST, user=user)
else:
edit_profile = EditProfileForm(user=user)
return render(request, 'main/profile.html', {'edit_profile': edit_profile, 'redeem': redeem})

return render(request, 'main/profile.html', {'edit_profile': edit_profile, 'redeem': redeem})
else:
return redirect('/')


forms.py

class CouponForm(forms.Form):
code = forms.CharField(required=True,
label=_("code"),
widget=forms.TextInput
(attrs={'placeholder':_('Code'),
'class': 'text-center'}))

def __init__(self, *args, **kwargs):
self.user = None
self.types = None
if 'user' in kwargs:
self.user = kwargs['user']
del kwargs['user']
if 'types' in kwargs:
self.types = kwargs['types']
del kwargs['types']
super(CouponForm, self).__init__(*args, **kwargs)

def clean_code(self):
code = self.cleaned_data['code']
try:
coupon = Coupon.objects.get(code=code)
except Coupon.DoesNotExist:
raise forms.ValidationError(_("This code is not valid."))
self.coupon = coupon

if self.user is None and coupon.user_limit is not 1:
# coupons with can be used only once can be used without tracking the user, otherwise there is no chance
# of excluding an unknown user from multiple usages.
raise forms.ValidationError(_(
"The server must provide an user to this form to allow you to use this code. Maybe you need to sign in?"
))

if coupon.is_redeemed:
raise forms.ValidationError(_("This code has already been used."))

try: # check if there is a user bound coupon existing
user_coupon = coupon.users.get(user=self.user)
if user_coupon.redeemed_at is not None:
raise forms.ValidationError(_("This code has already been used by your account."))
except CouponUser.DoesNotExist:
if coupon.user_limit is not 0: # zero means no limit of user count
# only user bound coupons left and you don't have one
if coupon.user_limit is coupon.users.filter(user__isnull=False).count():
raise forms.ValidationError(_("This code is not valid for your account."))
if coupon.user_limit is coupon.users.filter(redeemed_at__isnull=False).count(): # all coupons redeemed
raise forms.ValidationError(_("This code has already been used."))
if self.types is not None and coupon.type not in self.types:
raise forms.ValidationError(_("This code is not meant to be used here."))
if coupon.expired():
raise forms.ValidationError(_("This code is expired."))
return code


models.py

@python_2_unicode_compatible
class Coupon(models.Model):
value = models.IntegerField(_("Value"), help_text=_("Arbitrary coupon value"))
code = models.CharField(
_("Code"), max_length=30, unique=True, blank=True,
help_text=_("Leaving this field empty will generate a random code."))
type = models.CharField(_("Type"), max_length=20, choices=COUPON_TYPES)
user_limit = models.PositiveIntegerField(_("User limit"), default=1)
created_at = models.DateTimeField(_("Created at"), auto_now_add=True)
valid_until = models.DateTimeField(
_("Valid until"), blank=True, null=True,
help_text=_("Leave empty for coupons that never expire"))
campaign = models.ForeignKey('Campaign', verbose_name=_("Campaign"), blank=True, null=True, related_name='coupons')

objects = CouponManager()

class Meta:
ordering = ['created_at']
verbose_name = _("Coupon")
verbose_name_plural = _("Coupons")

def __str__(self):
return self.code

def save(self, *args, **kwargs):
if not self.code:
self.code = Coupon.generate_code()
super(Coupon, self).save(*args, **kwargs)

def redeem(self, user=None):
try:
coupon_user = self.users.get(user=user)
except CouponUser.DoesNotExist:
try: # silently fix unbouned or nulled coupon users
coupon_user = self.users.get(user__isnull=True)
coupon_user.user = user
except CouponUser.DoesNotExist:
coupon_user = CouponUser(coupon=self, user=user)
coupon_user.redeemed_at = timezone.now()
coupon_user.save()
redeem_done.send(sender=self.__class__, coupon=self)


Update - Error on Coupon.redeem (see view for code):

'QueryDict' object has no attribute 'users'

in models.py | coupon_user = self.users.get(user=user)

Answer

The way you are creating CouponForm instance is not valid. You are doing

redeem = CouponForm({'code': Coupon.code}, user=user)

In this you are passing initial data for code as Coupon.code which is not correct. You should pass valid value for code ie. some text. But here you are passing Coupon.code with is attribute of Coupon model, not expected value.


As your form has code field, you can pass the request.POST to instantiate the form with submitted values.

redeem = CouponForm(request.POST, user=user)

Also, you are instantiating the form multiple times (e.g. in if and else part of redeem.is_valid() ), don't think you need this.