Reputation: 189
I'm trying to understand Django DRF and am trying to do something complicated from my knowledge. In my application, I have invite keys, but I only want to allow the user to create an invite key after 3 days or if this is their first key. How do I create the serializer and view for this in a better way?
I'm using Django 2.1.5 and DRF 3.9.1
I have tried and read other questions on the web, but cannot get a good grasp on achieving what I want. All I have in mind is thin views, thick serializers. My view portion is not working correctly due to
Object of type 'AttributeError' is not JSON serializable
That error is coming from my view, that much I know.
My Model:
class Invite_Key(models.Model):
STATUS_CHOICES = (
('valid', 'valid'),
('not_valid', 'not_valid'),
('banned', 'banned'),
)
# Foreign keys
user_submitter = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
null=True,
blank=True,
related_name='user_submitter'
)
user_receiver = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
null=True,
blank=True,
related_name='user_receiver'
)
uuid = models.UUIDField(default=generate_ulid_as_uuid, editable=False, unique=True, null=False)
open_uuid = models.UUIDField(default=uuid1, editable=False, unique=True, null=False)
reason = models.TextField(validators=[bad_words, bad_words_inline, test_test])
# Now are choices
status = models.CharField(max_length=14, choices=STATUS_CHOICES, default="valid")
# Now below are boolean
is_taken = models.BooleanField(default=False)
is_locked = models.BooleanField(default=False)
claim_date = models.DateField(editable=True, null=True, blank=True)
expire_date = models.DateField(editable=True, null=True, blank=True)
# Now these are the time stamps
created_at = models.DateTimeField(auto_now_add=True, editable=False)
updated_at = models.DateTimeField(auto_now=True, editable=False)
def __str__(self):
return str(self.uuid)
# An instance of our object temp used for later
def __init__(self, *args, **kwargs):
super(Invite_Key, self).__init__(*args, **kwargs)
self.temp = ''
# We can set attributes onSave of our data to the table
def save(self, *args, **kwargs):
if not self.id:
# for expire date
self.expire_date = datetime.now() + timedelta(days=7)
super(Invite_Key, self).save(*args, **kwargs)
My Serializer:
class InviteKeyCreateAllSerializer(serializers.ModelSerializer):
current_user = serializers.SerializerMethodField('_user')
is_take = serializers.SerializerMethodField()
class Meta:
model = Invite_Key
exclude = [
'user_submitter',
'user_receiver',
'uuid',
'status',
'is_taken',
'is_locked',
'claim_date',
'expire_date',
]
# Use this method for the custom field
def _user(self, obj):
request = getattr(self.context, 'request', None)
if request:
return request.user
def get_is_taken(self, obj):
return False
def get_status(self, obj):
return 'valid'
# end of custom fields
def create(self, validated_data):
key = Invite_Key(
user_submitter=validated_data['request.user'],
)
key.set_user_submitter(validated_data['user_submitter'])
key.save()
return key
My View
class InviteKeyAllCreateView(generics.CreateAPIView):
"""
POST invitekey/
"""
serializer_class = InviteKeyCreateAllSerializer
permission_classes = (IsAuthenticated,)
def post(self, request, *args, **kwargs):
user = self.request.user
try:
# get user count of keys
cur_key_count = Invite_Key.objects.filter(user_submitter_id=user.id).order_by('-created_at').count()
if cur_key_count > 0:
# now we get the latest one
check_key = Invite_Key.object.filter(user_submitter_id=user.id)[0]
now = datetime.now()
if now-timedelta(days=3) <= datetime.datetime.strptime(check_key['created_at'], "%Y-%m-%dT%H:%M:%S.%fZ"):
serialized_data = InviteKeyCreateAllSerializer(data=request.data)
serialized_data.is_valid(raise_exception=True)
serialized_data.save()
else:
return Response(
data={
"message": "Sorry, you have recently created an Invite Key, try again some other time."
},
status=status.HTTP_400_BAD_REQUEST
)
except Exception as e:
return Response(
data={
"message": "The Invite Key could not be created.",
"error": e
},
status=status.HTTP_400_BAD_REQUEST
)
I expected an Invite Key to be created with creating an http post request, and the view would check if the user has made keys, then get the latest key, then check the key was 3 days old before creating a new one, and the serializer would populate the missing data from the post request.
The actual result was I get an AttributeError. Also, is it possible to move most of my view logic to the serializer? Can someone give me hints? I was looking for something similar in the DRF Docs for Serializers and Views and can't find much.
If I'm missing some info let me know, thanks...
EDIT: Added model class EDIT: I changed to str(e), and my error is now:
type object 'Invite_Key' has no attribute 'object'
EDIT: Added Error-Traceback
Django Version: 2.1.5
Python Version: 3.6.8
Installed Applications:
['django.contrib.admin.apps.SimpleAdminConfig',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'corsheaders',
'djmoney',
'api',
'main',
'rssparser',
'invite_system',
'user_system']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware']
Traceback:
File "/usr/local/lib/python3.6/site-packages/django/core/handlers/exception.py" in inner
34. response = get_response(request)
File "/usr/local/lib/python3.6/site-packages/django/core/handlers/base.py" in _get_response
126. response = self.process_exception_by_middleware(e, request)
File "/usr/local/lib/python3.6/site-packages/django/core/handlers/base.py" in _get_response
124. response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/usr/local/lib/python3.6/site-packages/django/views/decorators/csrf.py" in wrapped_view
54. return view_func(*args, **kwargs)
File "/usr/local/lib/python3.6/site-packages/django/views/generic/base.py" in view
68. return self.dispatch(request, *args, **kwargs)
File "/usr/local/lib/python3.6/site-packages/rest_framework/views.py" in dispatch
495. response = self.handle_exception(exc)
File "/usr/local/lib/python3.6/site-packages/rest_framework/views.py" in handle_exception
455. self.raise_uncaught_exception(exc)
File "/usr/local/lib/python3.6/site-packages/rest_framework/views.py" in dispatch
492. response = handler(request, *args, **kwargs)
File "/opt/service/api/src/invite_system/views.py" in post
229. check_key = Invite_Key.object.filter(user_submitter_id=user.id).order_by('-created_at')[0]
Exception Type: AttributeError at /api/v1/invitekey
Exception Value: type object 'Invite_Key' has no attribute 'object'
EDIT: I am debugging and am stuck on how to structure this if statement below.
Error:
'Invite_Key' object is not subscriptable
Point of Error:
now = datetime.now()
if now-timedelta(days=3) <= datetime.strptime(check_key['created_at'], "%Y-%m-%dT%H:%M:%S.%fZ"):
serialized_data = InviteKeyCreateAllSerializer(data=request.data)
The if statement cannot evaluate check_key['created_at'] to be formatted. WHat is the correct way to do this? I'm using Postgres for the timestamps.
Upvotes: 1
Views: 8993
Reputation: 88509
.......
except Exception as e:
return Response(
data={
"message": "The Invite Key could not be created.",
"error": e },
status=status.HTTP_400_BAD_REQUEST)
In the above snippet, the variable e
the object of any of the valid Pycthon Exception class and obviously its not serializable.
represent the object in string format
.......
except Exception as e:
return Response(
data={
"message": "The Invite Key could not be created.",
"error": str(e)
},
status=status.HTTP_400_BAD_REQUEST)
It should be objects
instead of object
(charecter s
is missing)
check_key = Invite_Key.objects.filter(user_submitter_id=user.id)[0]
Upvotes: 3