Kerosive Kerosive - 1 year ago 53
Python Question

'QueryDict' object has no attribute 'users'

So I am trying to redeem a coupon I have created on the backend and I am encountering an error in my

models.py
I am using the
django-coupons
package which has very little documentation and I am struggling a bit with it. Please help!

traceback:

Traceback (most recent call last):
File "/Users/krippee/Documents/Projects/lularippee/env/lib/python3.5/site-packages/django/core/handlers/exception.py", line 39, in inner
response = get_response(request)
File "/Users/krippee/Documents/Projects/lularippee/env/lib/python3.5/site-packages/django/core/handlers/base.py", line 187, in _get_response
response = self.process_exception_by_middleware(e, request)
File "/Users/krippee/Documents/Projects/lularippee/env/lib/python3.5/site-packages/django/core/handlers/base.py", line 185, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/Users/krippee/Documents/Projects/lularippee/main/views.py", line 198, in gift_card
Coupon.redeem(request.POST)
File "/Users/krippee/Documents/Projects/lularippee/coupons/models.py", line 124, in redeem
coupon_user = self.users.get(user=user)
AttributeError: 'QueryDict' object has no attribute 'users'


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)
else:
pass
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

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

Answer Source

Your problem is that you are calling instance method redeem on class but not on instance. Also you are expecting user but passing request.POST It should be

if redeem.is_valid():
    coupon = redeem.coupon
    coupon.redeem(request.user)

instead of

if redeem.is_valid():
    Coupon.redeem(request.POST)