This app collects info via form that is rendered by Django. The code posts data with JavaScript to viewset. Anybody can post to viewset but only admin users can view or edit them.
Please review my code and point out bad practices or errors.
views
"""
I've used serializers with inputs overrides as forms here
We can do this in other ways.
"""
def contact_form(request):
serializer = ContactMessageSerializer()
context = {
'serializer': serializer
}
return render(request, 'meant/contact.html', context)
class MessageViewSet(viewsets.ModelViewSet):
permission_classes = (AdminOnly,)
queryset = Contact.objects.all()
pagination_class = ContactMessagePaginator
serializer_class = ContactMessageSerializer
filterset_class = ContactMessageFilter
def create(self, request):
serializer = ContactMessageSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
serializers.py
TEMPLATE_PATH = os.path.join(
settings.TEMPLATES[0]["DIRS"][0], "base", "custom_input_forms.html"
)
CHOICE_CHECK = set(map(operator.itemgetter(0), Contact.SUBJECT))
class ContactMessageSerializer(serializers.ModelSerializer):
status = serializers.SerializerMethodField()
subject = serializers.ChoiceField(choices=Contact.SUBJECT)
name = serializers.CharField(
style={
"template": TEMPLATE_PATH,
"class": "custom_form_control",
"input_type": "input"
}
)
email = serializers.CharField(
style={
"template": TEMPLATE_PATH,
"class": "custom_form_control",
"input_type": "input"
}
)
subject = serializers.CharField(
style={
"template": TEMPLATE_PATH,
"class": "custom_form_control",
"input_type": "select",
"choice": Contact.SUBJECT
}
)
message = serializers.CharField(
style={
"template": TEMPLATE_PATH,
"input_type": "textarea",
}
)
def get_status(self, obj):
return obj.status
def validate(self, data):
name = data["name"]
email = data["email"]
choice = data["subject"]
if (5 > len(name)):
raise serializers.ValidationError({"Invalid name": "Name is to short."})
try:
validate_email(email)
except:
raise serializers.ValidationError({"Invalid Email": "Email is invalid, please recheck."})
if not (choice in CHOICE_CHECK):
choices_printable = ', '.join(CHOICE_CHECK)
raise serializers.ValidationError({"Invalid Choice": f"Choice field is not valid. Valid choices are: {choices_printable}"})
return data
class Meta:
model = Contact
fields = "__all__"
tests.py
class MessageViewSetTest(APITestCase):
def setUp(self) -> None:
self.admin_user = Client()
self.base_user = Client()
self.user = User.objects.create_superuser(
username='foobar',
email='[email protected]',
password='hatefoobarpeople')
self.admin_user.force_login(user=self.user)
self.message_dummy = {
'name': 'Test User',
'email': '[email protected]',
'subject': 'app',
'message': 'one test please'
}
def test_get_messages_admin(self):
response = self.admin_user.get(reverse('message-list'))
self.assertEqual(response.status_code, 200)
def test_get_messages_all(self):
response = self.base_user.get(reverse('message-list'))
self.assertEqual(response.status_code, 403)
def test_post_correct_payload(self):
response_base = self.base_user.post(reverse('message-list'), self.message_dummy)
response_admin = self.admin_user.post(reverse('message-list'), self.message_dummy)
self.assertEqual(response_base.status_code, 201)
self.assertEqual(response_admin.status_code, 201)
self.assertEqual(response_admin.data['name'], self.message_dummy['name'])
def test_post_incorrect_payload(self):
self.message_dummy['subject'] = "this cant work"
response_base = self.base_user.post(reverse('message-list'), self.message_dummy)
response_admin = self.admin_user.post(reverse('message-list'), self.message_dummy)
self.assertEqual(response_base.status_code, 400)
self.assertEqual(response_admin.status_code, 400)
"""
We can test serializers to but in this case I don't think thats nessesary
becouse we are doing full request-response cycle
Regardles of that here is one for fun
"""
def test_email_valid(self):
self.message_dummy['email'] = 'emaildomain.com'
serializer = ContactMessageSerializer(data=self.message_dummy)
self.assertFalse(serializer.is_valid())
self.assertEqual(set(serializer.errors.keys()), set(['Invalid Email']))
permissions
class AdminOnly(BasePermission):
def has_permission(self, request, view):
if view.action == 'create':
return True
return request.user.is_superuser
def has_object_permission(self, request, view, obj):
return view.action in ['list', 'retrieve', 'update', 'partial_update', ] and request.user.is_superuser