Kerosive Kerosive - 25 days ago 13
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

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