I'm new in DRF: ¿Can I create a filter in a function in Django Rest Framework, not created it in a Class, for use in a view? I'm trying a filter for search by: document and document_type.
Ty
My code:
#model
class Account(models.Model):
'''Modelo para la gestión de cuentas'''
document_type = models.CharField('Tipo documento', null=True, max_length=20, choices=DOCUMENT_TYPE)
document = models.CharField('Documento',null=True, max_length=18)
first_name = models.CharField('Nombres / razón social',null=True, max_length=100)
last_name = models.CharField('Apellidos', null=True, max_length=100)
phone = models.CharField('Teléfono fijo', max_length=7, null=True, blank=True)
cellphone = models.CharField('Celular', max_length=10, null=True, blank=True)
email = models.EmailField('Correo', max_length=100, null=True, blank=True )
address = models.CharField('Dirección', max_length=70, null=True, blank=True)
gender = models.CharField('Genero',null=True, max_length=1, choices=GENDER_TYPE)
affiliate_type = models.CharField('Tipo de afiliado',null=True, max_length=12, choices=AFFILIATE_TYPE)```
#view
```class AccountList(APIView):
"""
List all accounts, or create a new account....
"""
def get(self, request, format=None):
account = Account.objects.all()
serializer = AccountSerializer(account, many=True)
filter_class = AccountFilter
filter_backends = [filters.SearchFilter]
return Response({'data':serializer.data},status=status.HTTP_200_OK)
def post(self, request, format=None):
serializer = AccountSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response({'Message':'Registro creado exitosamente'}, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)```
#filter
class AccountFilter(django_filters.rest_framework.FilterSet):
class Meta:
model = Account
fields = {
'document': ['exact'],
'document_type': ['exact']
}
ordering_fields = ('document')
You can use self.filter_queryset(queryset) to filter queryset using filter class.
class AccountList(APIView):
def get(self, request, format=None):
self.filter_class = AccountFilter
self.filter_backends = [filters.SearchFilter]
account_qs = Account.objects.all()
account = self.filter_queryset(account_qs)
serializer = AccountSerializer(account, many=True)
return Response({'data':serializer.data},status=status.HTTP_200_OK)
Related
Searching is not working in the django rest framework. searching for the approved status of leave applied by a particular employee. But the filtering/ searching/ sorting is properly working in the admin user. but not in the employee side.
Views.py
class LeaveList(APIView):
authentication_classes = [SessionAuthentication, BasicAuthentication, TokenAuthentication]
permission_classes = [IsAuthenticated]
filter_backends = [DjangoFilterBackend,SearchFilter,OrderingFilter]
ordering_fields = ['apply_date','number_of_days','status']
search_fields = ['status']
filter_fields = ['status']
def get(self, request, format=None):
employee = request.user
filter_backends = [DjangoFilterBackend,SearchFilter,OrderingFilter]
search_fields = ['status']
queryset = LeaveApplicationModel.objects.filter(employee_id=employee.id)
serializer_class = LeaveApplicationsListSerializer(queryset, many=True)
return Response(data=serializer_class.data)
Serializers.py
class LeaveApplicationsListSerializer(serializers.ModelSerializer):
class Meta:
model = LeaveApplicationModel
exclude = ['id', 'employee']
postman request
postman request
expecting
You're not using the search_filters anywhere. GenericAPIView or any class that subclasses it, e.g. ListAPIView, will use it automatically. It's in the docs.
If I were you, I'd rather subclass ListAPIView and set the relevant available attributes correctly. You can overwrite get_queryset to filter for the employee:
class LeaveList(ListAPIView):
authentication_classes = [SessionAuthentication, BasicAuthentication, TokenAuthentication]
permission_classes = [IsAuthenticated]
filter_backends = [DjangoFilterBackend,SearchFilter,OrderingFilter]
ordering_fields = ['apply_date','number_of_days','status']
search_fields = ['status']
filter_fields = ['status']
serializer_class = LeaveApplicationsListSerializer
def get_queryset(self):
employee = self.request.user
return LeaveApplicationModel.objects.filter(employee_id=employee.id)
I am new to DRF. I am very interested in application of generic views. I want to rewrite the following code with generic DRF view RetrieveAPIView:
views.py
class ProductDetail(APIView):
def get_object(self, category_slug, product_slug):
try:
return Product.objects.filter(category__slug=category_slug).get(slug=product_slug)
except Product.DoesNotExist:
raise Http404
def get(self, request, category_slug, product_slug, format=None):
product = self.get_object(category_slug, product_slug)
serializer = ProductSerializer(product)
return Response(serializer.data)
urls.py
urlpatterns = [
path('products/<slug:category_slug>/<slug:product_slug>/',
ProductDetail.as_view())
]
models.py
class Category(models.Model):
name = models.CharField(max_length=255)
slug = models.SlugField()
class Meta:
ordering = ('name',)
def __str__(self):
return self.name
def get_absolute_url(self):
return f'/{self.slug}/'
class Product(models.Model):
category = models.ForeignKey(
Category, related_name='products', on_delete=models.CASCADE)
name = models.CharField(max_length=255)
slug = models.SlugField()
description = models.TextField(blank=True, null=True)
price = models.DecimalField(max_digits=6, decimal_places=2)
image = models.ImageField(upload_to='uploads/', blank=True, null=True)
thumbnail = models.ImageField(upload_to='uploads/', blank=True, null=True)
date_added = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ('-date_added',)
def __str__(self):
return self.name
def get_absolute_url(self):
return f'{self.category.slug}/{self.slug}/'
How can I use RetrieveAPIView instead of above APIView?
I tried to add a custom mixin, did not help:
class MultipleFieldLookupMixin:
def get_object(self):
queryset = self.get_queryset() # Get the base queryset
queryset = self.filter_queryset(queryset) # Apply any filter backends
multi_filter = {field: self.kwargs[field] for field in self.lookup_fields}
obj = get_object_or_404(queryset, **multi_filter) # Lookup the object
self.check_object_permissions(self.request, obj)
return obj
class ProductDetail(MultipleFieldLookupMixin, RetrieveAPIView):
queryset = Product.objects.filter(category__slug=category_slug).get(slug=product_slug)
serializer = ProductSerializer
lookup_fields = ['category_slug', 'product_slug']
Does not work.
I have troubles with my code in deleting a specific loan_id(primary key). My post method works good but my delete method doesn´t. Here's my code:
views.py:
class SolicitudPrestamoViewSet(viewsets.mixins.CreateModelMixin,viewsets.mixins.DestroyModelMixin,viewsets.GenericViewSet):
permission_classes = [IsAdminUser]
serializer_class = SucursalSerializer
queryset = Prestamo.objects.all()
lookup_field = 'loan_id'
def get(self, request, format=None,):
serializer = SucursalSerializer()
return Response(serializer.data)
def delete(self, request, loan_id, format=None):
object = self.get_object(loan_id)
object.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
models.py:
class Prestamo(models.Model):
loan_id = models.AutoField(primary_key=True)
loan_type = models.CharField(max_length=20,
choices = [('PERSONAL', 'PERSONAL'), ('HIPOTECARIO', 'HIPOTECARIO'), ('PRENDARIO', 'PRENDARIO')])
loan_date = models.DateField()
loan_total = models.IntegerField()
customer_id = models.IntegerField()
class Meta:
db_table = 'prestamo'
In viewset you should use destroy method to handle DELETE requests:
class SolicitudPrestamoViewSet(viewsets.mixins.CreateModelMixin,viewsets.mixins.DestroyModelMixin,viewsets.GenericViewSet):
permission_classes = [IsAdminUser]
serializer_class = SucursalSerializer
queryset = Prestamo.objects.all()
lookup_field = 'loan_id'
def destroy(self, request, loan_id):
object = self.get_object(loan_id)
object.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
I have a model Classroom which has a field code that is generated automatically
from django.utils.crypto import get_random_string
class Classroom(models.Model):
CODE_LEN = 16
teacher = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
name = models.CharField(max_length=250)
subject = models.CharField(max_length=50)
code = models.CharField(max_length=CODE_LEN, unique=True)
def save(self, *args, **kwargs):
while True:
self.code = get_random_string(length=16)
if not Classroom.objects.filter(code=self.code).exists():
break
super().save(*args, **kwargs)
The serializer for Classroom is -
class ClassroomSerializer(serializers.ModelSerializer):
class Meta:
model = Classroom
fields = ('teacher', 'name', 'subject', 'code')
This is the view for creating new Classroom -
class ListCreateClassroom(APIView):
def get(self, request):
classrooms = Classroom.objects.all()
serializer = ClassroomSerializer(classrooms, many=True)
return Response(serializer.data)
def post(self, request):
token = request.headers.get('Authorization').split()[1]
token_obj = AccessToken.objects.get(token=token)
teacher = token_obj.user.pk
request.data.update({"teacher": teacher})
serializer = ClassroomSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
When I send an api request to add new classroom, the request doesn't contain the code field because of which serializer.is_valid() throws error.
One way to solve it would be to create a code in the view and add it to the request.
But, is there a better way?
Simply set the code field in the serializer to read_only=True:
class ClassroomSerializer(serializers.ModelSerializer):
code = serializers.CharField(read_only=True)
class Meta:
model = Classroom
fields = ('teacher', 'name', 'subject', 'code')
In my project I have created a custom User object with a one to one relation to a Profile object in order to isolate authentication fields.
Profile
class Profile(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
image = models.ImageField(default='/user_images/default.png', upload_to='user_images', blank=True, null=True)
def __str__(self):
return self.first_name + " " + self.last_name
User
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(unique=True)
profile = models.OneToOneField(Profile, related_name="user" ,on_delete=models.CASCADE, default=None, blank=True, null=True)
is_visible = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
date_joined = models.DateTimeField(default=timezone.now)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
objects = CustomUserManager()
def __str__(self):
return self.email
My problem now is that i want to create the Profile object with the same request of the User object
View
class CustomView(APIView):
permission_classes = (permissions.AllowAny,)
def post(self, request, format='json'):
serializer = CustomSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Serializer
class CustomSerializer(serializers.Serializer):
# User
email = serializers.EmailField(required=True)
password = serializers.CharField(min_length=8, write_only=True)
# Profile
first_name = serializers.CharField(max_length=50)
last_name = serializers.CharField(max_length=50)
image = serializers.ImageField(allow_null=True, max_length=100, required=False)
def create(self, validated_data):
profile = Profile.objects.create(
first_name = validated_data['first_name'],
last_name = validated_data['first_name'],
image = validated_data['image'],
)
user = User.objects.create(
email = validated_data['email'],
profile = profile
)
user.set_password(validated_data['password'])
user.save()
return user
Now in the database everything is created correctly but i receive this error
Got AttributeError when attempting to get a value for field
first_name on serializer CustomSerializer. The serializer field
might be named incorrectly and not match any attribute or key on the
User instance. Original exception text was: 'User' object has no
attribute 'first_name'.
I suppose this means that a serializer can only handle the creation of one model so i'm supposed to handle my case?
Thanks
I assume that your error comes from to_representation() and what you can do is to set write_only=True on first_name, last_name and image fields like this:
first_name = serializers.CharField(max_length=50, write_only=True)
However, a more clean solution would be to use ModelSerialzier for both User and Profile models. If you do so, you'll have something like:
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = ("id", "first_name", "last_name", "image")
class UserSerializer(serializers.ModelSerializer):
profile = ProfileSerializer()
class Meta:
model = User
fields = ("id", "email", "profile", [...])
def create(self, validated_data):
...