How to use pre_save methods in rest_framework serializers? - django

I know this is a noob question about DRF. I use latest version of Django and DRF. In my Django , I create slugs in a method using pre_save signals.
def create_slug(instance, new_slug=None):
slug = slugify(instance.title)
if new_slug is not None:
slug = new_slug
qs = Article.objects.filter(slug=slug).order_by("-id")
exists = qs.exists()
if exists:
new_slug = "%s-%s" %(slug, qs.first().id)
return create_slug(instance, new_slug = new_slug)
return slug
#receiver(pre_save, sender = Article)
def pre_save_article_receiver(sender, instance, raw, using, **kwargs):
if not instance.slug:
instance.slug = create_slug(instance)
pre_save.connect(pre_save_article_receiver, sender=Article)
Then I can manage to write my views and serializers using DRF
from rest_framework import serializers
from yogavidya.apps.articles.models import Article
class ArticleSerializer(serializers.ModelSerializer):
class Meta:
model = Article
fields = [
"title",
"user",
"content",
"excerpt",
]
--views.py--
app_name = 'articles'
class ArticleListView(generics.ListCreateAPIView):
lookup_field = 'pk'
serializer_class = ArticleSerializer
queryset = Article.objects.all()
def list(self, request):
# Note the use of `get_queryset()` instead of `self.queryset`
queryset = self.get_queryset()
serializer = ArticleSerializer(queryset, many=True)
print(serializer.data)
return Response(serializer.data)
def get_queryset(self):
return Article.objects.all()
def get_object(self):
pk = self.kwargs.get("pk")
return Article.objects.get(pk=pk)
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
class ArticleViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows users to be viewed or edited.
"""
queryset = Article.objects.all().order_by('-title')
serializer_class = ArticleSerializer
When I post my form I get name 'create_slug' is not defined. Where is best place to override my save method and updates? can you help me ?
Thanks

I guess error is due to the wrong indentation of def create_slug
Class Xyz:
...
...
def create_slug(instance, new_slug=None):
slug = slugify(instance.title)
if new_slug is not None:
slug = new_slug
qs = Article.objects.filter(slug=slug).order_by("-id")
exists = qs.exists()
if exists:
new_slug = "%s-%s" %(slug, qs.first().id)
return create_slug(instance, new_slug = new_slug)
return slug
It is not a class function, so it should be outside class.

Related

How to Convert Api view class to CreateModelMixin

class Add_Product(APIView):
def post(self,request,*args, **kwargs):
user=request.user
if user.is_authenticated:
data=request.data
date=datetime.now().date()
slug=user.username+"-"f'{int(time())}'
print(data)
serializer=ProductSerializer(data=data,many=True)
if serializer.is_valid():
print(serializer.data)
serializer.save(user=request.user,slug=slug)
return Response("Your product is added")
return Response(serializer.errors)
return Response("Login First")
I want to convert this to CreateModelMixin But i don't know how to pass values like request.user and slug in create method.
class Product_List(GenericAPIView,CreateModelMixin):
queryset = Product.objects.all()
serializer_class = ProductSerializer
def post(self,request,*args, **kwargs):
return self.create(request,*args,**kwargs)
You can pass the user to the serializer through its context then override its create method:
# View
class Product_List(GenericAPIView,CreateModelMixin):
queryset = Product.objects.all()
serializer_class = ProductSerializer
permission_classes = (IsAuthenticated,)
def get_serializer_context(self):
return {'user': self.request.user}
# Serializer
class ProductSerializer(serializers.ModelSerializer):
[...]
def create(self, validated_data):
user = self.context['user']
slug = f'{user.username}-{int(time())}'
return Product.objects.create(
user=user,
slug=slug,
**validated_data
)

How to use multiple lookup fields in Django Rest framework in a class based view?

I literally searched similar questions but didn't a answer that solves my question
I want a functionality like a question can have multiple answers
my models.py
class QuestionModel(models.Model):
question = models.TextField()
author = models.ForeignKey(User, on_delete=models.CASCADE)
doubt_class_id = models.ForeignKey(DoubtClasses, on_delete=models.CASCADE)
conceptual_class_id = models.ForeignKey(LiveClass_details, on_delete=models.CASCADE, null=True, blank=True)
status = models.BooleanField(default=False)
mentor = models.ForeignKey(Mentor, on_delete=models.CASCADE, null=True, blank=True)
def __str__(self):
return self.question
def clean(self):
if self.doubt_class_id and self.conceptual_class_id :
raise ValidationError("only one field can be set")
class AnswersModel(models.Model):
answer = models.TextField()
question_id = models.ForeignKey(QuestionModel, on_delete=models.CASCADE)
my urls.py
path('questions/', views.QuestionModelView.as_view()),
path('questions/<int:id>/', views.QuestionModelViewID.as_view()),
path('questions/<int:id>/answer/', views.AnswerModel.as_view()),
my views.py
# Create your views here.
class QuestionModelView(mixins.ListModelMixin, mixins.CreateModelMixin,GenericAPIView):
queryset = models.QuestionModel.objects.all()
serializer_class = serializers.QuestionModel_serializer
permission_classes = [IsAuthenticated]
def get(self, request):
return self.list(request)
def post(self, request):
return self.create(request)
class QuestionModelViewID(mixins.ListModelMixin, mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin,GenericAPIView):
queryset = models.QuestionModel.objects.all()
serializer_class = serializers.QuestionModel_serializer
permission_classes = [IsAuthenticated]
lookup_field = 'id'
def get(self, request, id=None):
if id:
return self.retrieve(request, id)
else:
return Response(status=status.HTTP_204_NO_CONTENT)
def put(self, request, id=None):
if int(request.POST.get('author')) != self.request.user.id:
return Response("you cannot edit othe user question", status=status.HTTP_405_METHOD_NOT_ALLOWED)
if id:
return self.update(request, id)
else:
return Response(status=status.HTTP_204_NO_CONTENT)
def delete(self, request, id=None):
if int(request.POST.get('author')) != self.request.user.id:
return Response("you cannot destroy othe user question", status=status.HTTP_405_METHOD_NOT_ALLOWED)
if id:
return self.delete(request, id)
else:
return Response(status=status.HTTP_204_NO_CONTENT)
class AnswerModel(mixins.ListModelMixin, mixins.CreateModelMixin, GenericAPIView):
lookup_field = 'id'
serializer_class = serializers.AnswerModel_serializer
def get_queryset(self, *args, **kwargs):
return models.AnswersModel.objects.filter(question_id = self.kwargs['id'])
def get(self, request, id=None):
return self.list(request)
def post(self, request, id=None):
if request.user.is_superuser:
return self.create(request, id)
else:
return Response("only superuser can post a answer", status=status.HTTP_400_BAD_REQUEST)
I want to make a url like questions/id/answers/answer_id where id is the question id and answer_id is the id for a particular answer as a question can have multiple answers but for that i want to add 2 lookup_fields in my view which i am not getting how to add it or please suggest another approach for this query if its not possible to do like that
if you have query like this questions/id/answers/answer_id
you can do like this if I understood your question good.
def get_queryset(self):
id = self.kwargs['id']
answerid = self.kwargs['answer_id']
queryset = Model.objects.get(
id=id,answerid=answerid)
return queryset
or
def get(self, request, id, answer_id):
Questiomn = Model.objects.get(
id=id,answerid=answerid)

Django with Postgresql update object, prevent order re-ordering

in django production mode with postgreSQL, When I got an still existing object/row from the database, edit/update it and store it back, the row is moved to the end of the table.
object 1
object 2
object 3
after done editing object 1
object 2
object 3
object 1
models.py
class itemModels(models.Model):
name = models.CharField(max_length=50)
photo = models.ImageField(max_length=200, blank=True, default="default.png")
photo2 = models.ImageField(max_length=200, blank=True, default="default2.png")
price = models.DecimalField(max_digits=6, decimal_places=2)
sku = models.CharField(max_length=50, unique=True)
description = models.CharField(max_length=255)
def __self__(self):
return self.name
views.py
class itemViewSet(viewsets.ModelViewSet):
queryset = itemModels.objects.all()
serializer = itemSerializers()
renderer_classes = [TemplateHTMLRenderer]
template_name = 'product-list.html'
lookup_field = 'pk'
# list all item
def list(self, request):
queryset = itemModels.objects.all()
context = {'posts': queryset}
return Response(context)
# serializer form
def serializer_list(self, request):
serializer = itemSerializers()
return Response({'serializer': serializer}, template_name='product-create.html')
def retrieve(self, request, pk):
profile = get_object_or_404(itemModels, pk=pk)
serializer = itemSerializers(profile)
return Response({'serializer': serializer}, template_name='product-edit.html')
def update(self, request, pk):
profile = get_object_or_404(itemModels, pk=pk)
serializer = itemSerializers(profile, data=request.data)
if serializer.is_valid():
serializer.save()
print('status: status.Item Update')
return redirect('item')
else:
print('status: status.Item Bad Update')
i want to prevent this re-ordering happen, is there something i miss out ? thanks guys
Note:
if i am using development mode and switch the to django dummie database, this wont happen.
def list(self, request):
queryset = itemModels.objects.all()
context = {'posts': queryset}
return Response(context)
to
def list(self, request):
queryset = itemModels.objects.order_by('id').all()
context = {'posts': queryset}
return Response(context)
credit for #snakecharmerb thanks

DRF: django rest framework case insensitive lookup in detail view

How to make urls case insensitive with certain parameters passed
For example, assuming Stock model has a ticker. All links below should find the same ticker content, right now they are case sensitive and try to search for different values:
/stocks/AAPL
/stocks/aapl
/stocks/AaPl
views.py
class StockViewSet(viewsets.ModelViewSet):
queryset = Stock.objects.all()
serializer_class = StockSerializer
lookup_field = "ticker"
#action(detail=True, methods=["get"], url_path="is", url_name="is")
def get_income_statement(self, request, *args, **kwargs):
is_qs = IncomeStatement.objects.filter(ticker=self.get_object())
serializer = IncomeStatementSerializer(is_qs, many=True)
return Response(serializer.data)
#action(detail=True, methods=["get"], url_path="bs", url_name="bs")
def get_balance_sheet(self, requests, *args, **kwargs):
bs_qs = BalanceSheet.objects.filter(ticker=self.get_object())
serializer = BalanceSheetSerializer(bs_qs, many=True)
return Response(serializer.data)
#action(detail=True, methods=["get"], url_path="cf", url_name="cf")
def get_cashflows_statement(self, requests, *args, **kwargs):
cf_qs = CashflowsStatement.objects.filter(self.get_object())
serializer = CashflowsStatementSerializer(cf_qs, many=True)
return Response(serializer.data)
class IncomeStatementDetail(viewsets.ModelViewSet):
queryset = IncomeStatement.objects.all()
serializer_field = IncomeStatementSerializer
class BalanceSheetDetail(viewsets.ModelViewSet):
queryset = BalanceSheet.objects.all()
serializer_field = BalanceSheetSerializer
class CashflowsStatementDetail(viewsets.ModelViewSet):
queryset = CashflowsStatement.objects.all()
serializer_field = CashflowsStatementSerializer
urls.py
router = DefaultRouter()
router.register(r"stocks", views.StockViewSet)
urlpatterns = router.urls
models.py
class Stock(models.Model):
id = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
ticker = models.CharField(max_length=10, unique=True, primary_key=True)
slug = models.SlugField(default="", editable=False)
def save(self, *args, **kwargs):
value = self.ticker
self.slug = slugify(value, allow_unicode=True)
super().save(*args, **kwargs)
def __str__(self):
return self.ticker
class Meta:
verbose_name = "stock"
verbose_name_plural = "stocks"
ordering = ["ticker"]
Use lookup_url_kwarg and lookup_field as
from rest_framework import viewsets
class StockViewSet(viewsets.ModelViewSet):
lookup_url_kwarg = 'ticker'
lookup_field = 'ticker__iexact'
# rest of your code
You can refer the source code of get_object(self) to see how DRF fetching the model object in the detail view.

In ModelSerializer self.context doesn't have request , how to get this

my serialiser class is:
class NakshatraDateSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(read_only=True)
is_note_present = serializers.SerializerMethodField(read_only=True)
def get_is_note_present(self, nakshatra_date ):
user = None
request = self.context.get("request")
print (str(request))
if request and hasattr(request, "user"):
user = request.user
# user = serializers.SerializerMethodField('_user')
if user is None:
logging.error("user is none")
return False
try:
nakshatra_date_note = Notes.objects.all().get(nakshatra_date=nakshatra_date, created_by=user)
except ObjectDoesNotExist:
nakshatra_date_note = None
if nakshatra_date_note is None:
logging.error("no note present for this nakshatra date")
return False
logging.error(str(nakshatra_date_note))
return True
date = serializers.DateField(
required=True,
error_messages={
"date": "Nakshatra for date already present",
},
validators=[UniqueValidator(queryset=NakshatraDate.objects.all(), message="Nakshatra for date already present
")]
)
class Meta:
model = NakshatraDate
fields = (
'id', 'is_note_present', 'date', 'updated', 'created')
vewclass is :
class NakshatraDateViewSet(viewsets.ModelViewSet):
"""
Nakshatra dates
"""
permission_classes = (AllowAny,)
queryset = NakshatraDate.objects.all()
serializer_class = NakshatraDateSerializer
pagination_class = LargeResultsSetPagination
filter_backends = (django_filters.rest_framework.DjangoFilterBackend,)
filter_class = NakshatraDateFilter
def filter_queryset(self, queryset):
queryset = super(NakshatraDateViewSet, self).filter_queryset(queryset)
return queryset.order_by('-id')
def perform_create(self, serializer):
serializer.save()
i am trying to set value 'True' in variable is_note_present if a Note is present on a particular date. or else 'False'. But i am not able to get request object in self.context.
class Notes(models.Model):
date = models.DateField(null=False)
updated = models.DateTimeField(auto_now=True)
created = models.DateTimeField(auto_now_add=True, editable=False)
created_by = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='notes', on_delete=models.CASCADE)
i tried various way to get request . some of them aredef get_serializer_context(self):
return {'request': self.request}
and trien to context (context={'request':request}) with serialize_class variable in modelviewset
still i am not able to get the request in self.context
Your view should be like this.
class NakshatraDateViewSet(viewsets.ModelViewSet):
"""
Nakshatra dates
"""
permission_classes = (AllowAny,)
serializer_class = NakshatraDateSerializer
pagination_class = LargeResultsSetPagination
filter_backends = (django_filters.rest_framework.DjangoFilterBackend,)
filter_class = NakshatraDateFilter
def get_queryset(self, queryset):
queryset = NakshatraDate.objects.all()
return queryset.order_by('-id')
def get_serializer_context(self):
return {'request': self.request}
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(data=serializer.data)
return Response(status=status.HTTP_400_BAD_REQUEST)
You can see further about viewsets here.