Django rest Framework error messages. Does not work - python

I am new to Django and I am working on a small project, I want an error message to be shown if the user let the field empty. the code that I wrote is not working. Can anyone help me ?
def validate_name(school: School):
if school.name is None:
raise APIException(detail='Name is mandatory.')
class SchoolService(object):
#staticmethod
def validate_create(school: School):
validate_name(school)

Django Rest Framework provides default messages for such common wanted behaviours.
You do not even need to add anything to your field as fields are required by default, unless you explicitly specify required=False
If the user does not fill that field, DRF will automatically return a json object mentioning the field is required and should be filled.
see docs

In your serializer class try adding the validation this way
from django.core.exceptions import ValidationError
def validate_name(self, value):
if value is None:
raise serializers.ValidationError('Name is mandatory')
return value

Related

Errors including the custom model method in Django admin

What I tried to do
I tried to make custom admin method to include product thumbnail preview in my admin panel. product_thumbnail is a ImageField inside the Product Model which has general information of each products that I upload on the admin panel.
I wanted to make product_thumbnail as not required. Therefore, I set null=True and blank=True in my models.py also.
class Product(TimeStampModel):
...
product_thumbnail = models.ImageField(verbose_name='상품사진', null=True, blank=True, upload_to='product_thumbnail')
I created the custom function as below and included it in list_display admin option inside of ProductAdmin model.
def thumbnail_preview(self, obj):
return mark_safe(f'<img src="{obj.product_thumbnail.url}" width="40%" />')
thumbnail_preview.short_description = '상품사진'
Which error did I get?
I got the error as below:
ValueError: The 'product_thumbnail' attribute has no file associated with it.
What I have tried to solve issue
This might be because some Product may not have product_thumbnail so that Django admin failed to load its url due to its null value.
I used try-except so that if Django fails to find product_thumbnail associated with that product, then it can throw error text.
def thumbnail_preview(self, obj):
try:
return mark_safe(f'<img src="{obj.product_thumbnail.url}" width="40%" />')
except obj.product_thumbnail is None:
return HttpResponse('No images')
thumbnail_preview.short_description = '상품사진'
Then I got another error as below:
TypeError: catching classes that do not inherit from BaseException is not allowed
I already applied migrations. How can I solve this problem?
You write except obj.product_thumbnail is None:, but this is very incorrect! After the except keyword you are supposed to specify an exception class, not write some condition. Better yet why even use exceptions here? Generally one should avoid exceptions when one can do so, considering that if a FileField / ImageField has no file then they are considered falsy you can write the below code to solve your problem:
def thumbnail_preview(self, obj):
if obj.product_thumbnail:
return mark_safe(f'<img src="{obj.product_thumbnail.url}" width="40%" />')
return HttpResponse('No images')
thumbnail_preview.short_description = '상품사진'
This means that file is not uploaded in field at all, but you are trying to get its URL which is impossible of course, just wrap in condition. See another way to fix with using templates:
https://fixexception.com/django/the-s-attribute-has-no-file-associated-with-it/

How should i auto fill a field and make it readonly in django?

I`m new to django and i was doing a test for my knowledge.
Found a lot of duplicates in here and web but nothing useful
I'm trying to make a ForeignKey field which gets filled due to the other fields that user fills, and make it unchangeable for the user.
I thought that I should use overriding save() method but couldn't figure that at all.
How should I do that auto-fill and read-only thing?
Your approach is right. Override the save method and if self.pk is not None raise an exception if your field has changed. You can use django model utils to easily track changes in your model: https://django-model-utils.readthedocs.io/en/latest/utilities.html#field-tracker
Principle:
class MyModel(models.Model):
#....
some_field = models.Foreignkey(...)
tracker = FieldTracker()
def save(*args, **kwargs):
if self.pk is None:
# new object is being created
self.some_field = SomeForeignKeyObject
else:
if self.tracker.has_changed("some_field"):
raise Exception("Change is not allowed")
super().save(*args, **kwargs)

Correct way to validate GET parameters in django

I'm building a social website that uses django templates/dynamic pages (no SPA technology in place).
I have some ajax calls that check the users news feed or new messages.
Example GET web request of those looks as follows:
GET /feeds/check/?last_feed=3&feed_source=all&_=1500749662203 HTTP/1.1
This is how I receive it in the view:
#login_required
#ajax_required
def check(request):
last_feed = request.GET.get('last_feed')
feeds = Feed.get_feeds_after(last_feed)
It all works, but I want to protect it so the function get_feeds_after does not crash when a malicious user sets the GET parameter to last_feed="123malicious4556". Currently it crashes because in the Feed model the function does this:
#staticmethod
def get_feeds_after(feed):
feeds = Feed.objects.filter(parent=None, id__gt=float(feed))
return feeds
and crashes with the error:
ValueError at /feeds/check/
invalid literal for float(): 2fff2
I currently solve this by directly performing checks on the GET variable and handling exception on int() casting:
def check(request):
last_feed = request.GET.get('last_feed')
try:
feed_source = int(request.GET.get('last_feed'))
except ValueError:
return HttpResponse(0)
My question is what is the best django-recommended way to address this?
I know django has special support forms validation. But this does not seem quite right here, as the GET calls are more of an api rather than forms so it seems like a bad idea to define forms for those GET parameters.
Thanks
All you actually need are the form fields which do all basic validation for you.
If you need custom validation you can write your own validator or even better your own custom form field.
To use the field alone without the form you can do like that for example:
evalType = forms.CharField().clean(request.GET.get('eval-type'))
Because calling this way is not very human friendly I prefer to write a function to deal with it:
def cleanParam(params, paramName, FieldType, *args, **kwargs):
field = FieldType(*args, **kwargs)
cleaned = field.clean(params.get(paramName))
return cleaned
Which we use this way:
evalType = cleanParam(request.GET, 'eval-type', forms.CharField)
This will save you a form class. But I don't think it's very ugly to create a django form for that. A bit too much for the problem but no great concern IMHO.
The advantage of having a form is that you declare the fields you expect in your api call and can check all at once then see the result of is_valid().
I hope this helps.
You can also use a django Validator, which is
a callable that takes a value and raises a ValidationError if it does not meet some criteria.
In your specific case, you can use a combination of validate_slug() - which does not raise a ValidationError only if the passed input is composed of letters, numbers, underscores or hyphens - and int() functions in this way:
from django.core.validators import validate_slug
...
last_feed = request.GET.get("last_feed", "")
try:
validate_slug(last_feed)
last_feed = int(last_feed)
...
except ValueError:
print("invalid literal passed to int()")
except ValidationError:
print("invalid input passed to validate_slug()")
...
Request params can be validated with the DRF serializer.
Create a serializer with all the parameters that are required to be validated.
from rest_framework import serializers
class OrderIDSerializer(serializers.Serializer):
order_id = serializers.CharField(required=True)
def validate(self, attrs):
order_id = attrs.get('order_id')
if not Order.objects.filter(
id=order_id).exists():
raise error
attrs['order_id'] = order_id
return attrs
Import serializer in the views:
from order.serializers.order_serializer import OrderIDSerializer
serializer = OrderIDSerializer(data=request.query_params)
if serializer.is_valid():
order_id = serializer.data['order_id']
// your logic after the validation

handling unique constraint for custom field in django admin

Hi I'm new to django and just took my first foray into using a custom field for my model. I have a Char field that I want to always be saved as lower case so I implemented it as a custom field as follows (learned from another Stack Overflow post):
from django.db import models
from django.db.models.fields import CharField
class LowercaseCharField(CharField):
def pre_save(self, model_instance, add):
current_value = getattr(model_instance, self.attname)
setattr(model_instance, self.attname, current_value.lower())
return getattr(model_instance, self.attname)
class Item(models.Model):
name = LowercaseCharField(max_length=50, unique=True)
def __str__(self):
return self.name
I've tested this out in admin and indeed a field entry gets correctly converted to lowercase before it's saved. Unfortunately, when I tested the uniqueness constraint, admin isn't handling the Integrity Error gracefully. Instead of getting the clean error like I do if it's an exact case match from the get go:
I get the ugly error page:
How do I go about setting the custom field in such a way that the unique constraint is caught "early" enough to trigger the graceful error, or otherwise modify the admin so that this "later" error is handled more gracefully?
(Note: I am just using sqlite3 for my db at the moment)
UPDATE:
In case any one is interested, here's the modified code that worked for me:
class LowercaseCharField(CharField):
def get_db_prep_value(self, value, connection, prepared=False):
return value.lower()
I don't think you'll make it by overriding pre_save, because pre_save gets called after uniqueness validation has occurred.
Try with the other methods, such as get_db_prep_save or get_db_prep_value.

In Django how to avoid boilerplate code for getting model instance by pk in a view

Here is example code:
def someview(request):
try:
instance = SomeModel.objects.get(id=request.GET.get('id'))
except SomeModel.DoesNotExist:
instance = None
except ValueError:
# This error may occur if user manually enter invalid (non-integer)
# id value (intentionally or not) in a browser address bar, e.g.
# http://example.com/?id=2_foo instead of http://example.com/?id=2
# This raises ValueError: invalid literal for int() with base 10: '2_'
instance = None
...
Is there a best practice to get a model instance by pk without writing this boilerplate code over and over? Should I use some predefined shortcut in Django or just roll my own?
I was sure that I should use Django's DetailView or SingleObjectMixin but curiously enough it doesn't handle the ValueError exception from my example https://github.com/django/django/blob/master/django/views/generic/detail.py#L50
Is it implied that I have to specify correct integer regexp for pk kwarg in urlconf? Ok, likely. But what if I get pk from request querystring?
UPD I have special logic to do with instance either it's None or not.
You can also use Django's built in shorcut get_object_or_404() that it's designed for this specifically. That function will raise an Http404 exception in case the object doesn't exist. If you want to get None instead of raising the exception, you can create a helper function to accomplish it very easily:
def get_object_or_none(klass, *args, **kwargs):
try:
return get_object_or_404(klass, *args, **kwargs)
except Http404:
return None
Hope this helps!
The first part of your try/except block can be simplified by using django-annoying:
from annoying.functions import get_object_or_None
instance = get_object_or_None(SomeModel, id=request.GET.get('id'))
FYI, you can also just extract get_object_or_None from the package (see source).
There are many generic class based views that might be helpful, in your case DetailView could work.
from django.views.generic.detail import DetailView
class SomeModelDetailView(DetailView):
model = SomeModel
You can overwrite get_object method to change default behaviour.
def get_object(self, queryset=None):
return SomeModel.objects.get(pk=self.get_pk())
And lastly if object is none you should probably display custom 404 page.

Categories