How create trigger in Django Rest Framework to change booleanfield? - django

I have a question in django rest framework. Since I'm learning how to use some advanced options, I do not quite understand. I need to currently change a booleanfield every time a foreignkey is inserted into table.
How can I do this in model ?
Model:
class Persona(models.Model):
name = models.CharField(max_length=32)
cart = models.ForeignKey(Credit,null=True)
rg = models.IntergerField()
end = models.CharField(max_length=256)
details = models.TextField(blank=True, null=True)
order = models.ForeignKey(Order, null=True)
def __str__(self):
return self.cart
class Meta:
db_table='person'
app_label = 'bank'
class Credit(models.Model):
number = models.CharField(max_length=16, unique=True)
create_at = models.DateField(auto_add_now=True)
updated_at = models.DateField()
available = models.BooleanField()
def __str__(self):
return self.number
class Meta:
db_table = 'credit'
app_label = 'bank'
Serializer:
class PersonaSerializer(serializers.ModelSerializer):
order__id = serializers.ReadOnlyField(source='order.id')
class Meta:
model = Persona
fields = '__all__'
class Persona(viewsets.ModelViewSet):
allowed_methods = ('GET', 'POST', 'PUT', 'PATCH')
queryset = Persona.objects.all()
serializer_class = PersonaSerializer

You can override the create method on the ModelSerializer to achieve this:
def create(self, validated_data):
cart = validated_data['cart']
persona = super(PersonaSerializer, self).create(validated_data)
cart.available = True # update boolean
cart.save()
return persona
You can read more about this in the docs
If you want to handle this in your model you can override the Persona model save method:
class Persona(models.Model):
name = models.CharField(max_length=32)
cart = models.ForeignKey(Credit,null=True)
rg = models.IntergerField()
end = models.CharField(max_length=256)
details = models.TextField(blank=True, null=True)
order = models.ForeignKey(Order, null=True)
def __str__(self):
return self.cart
class Meta:
db_table='person'
app_label = 'bank'
def save(self, *args, **kwargs):
# Will only update the available field when the Persona
# instance is created and the cart field is not null
if not self.pk and self.cart:
self.cart.available = True
self.cart.save()
return super(Persona, self).save(*args, **kwargs)

Related

How to filter in Django Rest Framework function based view?

So many documentation for filtering in Django rest framework but all the examples are in class based view. but I am trying to do the same in DRF function based view. I wanted to do multiple filter for my items queryset.
I tried one way and it is working perfectly. Here first I am trying to search by item name or restaurant name in one request. then I take another keyword and try to filter restaurant name or item name based on restaurant city. It is working perfectly like if I hit this url
http://localhost:8000/api/items/?keyword=lasagne&keyword1=paris
then it gives me the perfect response.
But What I am asking for is that now my code looks for this specific part is messy and I want to add more fields for multiple filtering. Which procedure to follow? Should I follow this one and multiple requests and trying to filter from this.
Suppose now I want to filter the queryset based on dish_type, price, item_type, location and then search for items by name or restaurants by name
#this is my models
class Restaurant(models.Model):
user = models.OneToOneField(CustomUser, related_name='restaurant', on_delete=models.CASCADE, null=False)
name = models.CharField(max_length=200, blank=True, null=True)
profile_picture = models.ImageField(null=True, blank=True)
address = models.TextField(max_length=2000, blank=True, null=True)
city = models.CharField(max_length=200)
latitude = models.DecimalField(max_digits = 13, decimal_places = 7, blank=True, null=True)
longitude = models.DecimalField(max_digits = 13, decimal_places = 7, blank=True, null=True)
is_verified = models.BooleanField(default=False)
createdAt = models.DateTimeField(auto_now_add=True)
def __str__(self):
return str(self.name)
class Item(models.Model):
user = models.ForeignKey(Restaurant, on_delete=models.CASCADE)
name = models.CharField(max_length=220)
image = models.ImageField(null=True, blank=True)
dish_type = models.ForeignKey(Dishtype, on_delete=models.CASCADE)
item_type = models.ForeignKey(Itemtype, on_delete=models.CASCADE)
description = models.TextField(max_length=10000)
rating = models.DecimalField(max_digits=7, decimal_places=2, blank=True, null=True)
numReviews = models.IntegerField(null=True, blank=True, default=0)
old_price = models.DecimalField(max_digits=11, decimal_places=2)
discount = models.IntegerField(blank=True, null=True)
price = models.DecimalField(max_digits=12, decimal_places=2, blank=True, null=True)
countInStock = models.IntegerField(blank=True, null=True, default=0)
createdAt = models.DateTimeField(auto_now_add=True)
_id = models.AutoField(primary_key=True, editable=False)
def save(self, *args, **kwargs):
self.price = Decimal(self.old_price * (100 - self.discount) / 100)
return super(Item, self).save(*args, **kwargs)
class Meta:
ordering = ['-createdAt']
def __str__(self):
return self.name
#serializer
class RestaurantSerializer(serializers.ModelSerializer):
user = serializers.SerializerMethodField(read_only=True)
class Meta:
model = Restaurant
fields = '__all__'
def get_user(self, obj):
user = obj.user
serializer = UserSerializer(user, many=False)
return serializer.data
class ItemSerializer(serializers.ModelSerializer):
user = serializers.SerializerMethodField(read_only=True)
dish_type = serializers.SerializerMethodField(read_only=True)
item_type = serializers.SerializerMethodField(read_only=True)
class Meta:
model = Item
fields = '__all__'
def get_user(self, obj):
user = obj.user
serializer = RestaurantSerializer(user, many=False)
return serializer.data
def get_dish_type(self, obj):
dish_type = obj.dish_type
serializer = DishtypeSerializer(dish_type, many=False)
return serializer.data
def get_item_type(self, obj):
item_type = obj.item_type
serializer = ItemtypeSerializer(item_type, many=False)
return serializer.data
#views.py
#api_view(['GET'])
#permission_classes([IsAuthenticated])
def getItems(request):
user = request.user
query = request.query_params.get('keyword')
if query == None:
query = ''
cuery = request.query_params.get('keyword1')
if cuery == None:
cuery = ''
items = Item.objects.select_related('user').select_related('dish_type').select_related('item_type').all().filter(
Q(name__icontains = query) | Q(user__name__icontains = query))
else:
restaurant_city = Item.objects.select_related('user').select_related('dish_type').select_related('item_type').all(
).filter(Q(user__city__iexact = cuery))
items = restaurant_city.filter(Q(name__icontains = query) | Q(user__name__icontains = query))
serializer = ItemSerializer(items, many=True)
return Response(serializer.data)
######Updated solved the problem
#filters.py
class ItemFilter(django_filters.FilterSet):
numReviews = django_filters.NumberFilter()
numReviews__gt = django_filters.NumberFilter(field_name='numReviews', lookup_expr='gt')
numReviews__lt = django_filters.NumberFilter(field_name='numReviews', lookup_expr='lt')
name = django_filters.CharFilter(lookup_expr='icontains')
class Meta:
model = Item
fields = ['_id', 'dish_type__id']
#views
#api_view(['GET'])
#permission_classes([IsAuthenticated])
def getItems(request):
user = request.user
queryset = Item.objects.all()
filterset = ItemFilter(request.GET, queryset=queryset)
if filterset.is_valid():
queryset = filterset.qs
serializer = ItemSerializer(queryset, many=True)
return Response(serializer.data)
now data are passing like this
http://localhost:8000/api/items/?numReviews__gt=20&numReviews__lt=22
You can use queryset and override get_queryset function.
class FooViewSet(GenericViewSet, mixins.ListModelMixin):
authentication_classes = [JSONWebTokenAuthentication]
permission_classes = [IsAuthenticated]
serializer_class = ItemSerializer
def get_queryset(self):
query = self.request.query_params.get('keyword', '')
if not self.request.query_params.get('keyword1'):
items = Item.objects.select_related('user').select_related('dish_type').select_related(
'item_type').all().filter(
Q(name__icontains=query) | Q(user__name__icontains=query))
else:
restaurant_city = Item.objects.select_related('user').select_related('dish_type').select_related(
'item_type').all(
).filter(Q(user__city__iexact=self.request.query_params.get('keyword1', '')))
items = restaurant_city.filter(Q(name__icontains=query) | Q(user__name__icontains=query))
return items

Django ModelChoiceField: filtering object based on pk in url

I've read many questions about this topic, but none of the methods work for me.
There are 3 related models:
class Trips(models.Model):
lake = models.CharField("Lake", max_length=150)
city = models.CharField("City", max_length=100, blank=True)
s_date = models.DateTimeField("Starting Date", auto_now=False, auto_now_add=False)
e_date = models.DateTimeField("Ending Date", auto_now=False, auto_now_add=False)
trip_id = models.AutoField(primary_key=True)
class Meta:
verbose_name = "Trip"
verbose_name_plural = "Trips"
def __str__(self):
return f"{self.lake}-{self.trip_id}-{self.s_date}"
class Fisherman(models.Model):
name = models.CharField("Fisherman", max_length=50)
trip = models.ForeignKey(Trips, on_delete=models.CASCADE)
fisherman_id = models.AutoField(primary_key=True)
class Meta:
verbose_name = "Fisherman"
verbose_name_plural = "Fishermen"
def __str__(self):
return f"{self.name}-{self.fisherman_id}"
class Catch(models.Model):
fish_type = models.CharField("Fish Type", max_length=50)
catch_id = models.AutoField(primary_key=True)
weight = models.DecimalField("Weight", max_digits=5, decimal_places=2)
length = models.DecimalField("Length", max_digits=5, decimal_places=2, blank=True, null=True)
datetime = models.DateTimeField("Catch Time", auto_now=False, auto_now_add=False)
fisherman = models.ForeignKey(Fisherman, on_delete=models.CASCADE)
trip = models.ForeignKey(Trips, on_delete=models.CASCADE)
class Meta:
verbose_name = "Catch"
verbose_name_plural = "Catches"
def __str__(self):
return f"{self.fish_type}-{self.catch_id}"
I have a ModelForm to create a new catch. Here I use a ModelChoiceField to list Fishermen, but I don't know how to filter them. I only want display those who belong to the trip.
class CatchForm(forms.ModelForm):
fisherman = forms.ModelChoiceField(queryset= Fisherman.objects.all())
class Meta:
model = Catch
fields = ["fish_type", "weight", "length", "datetime", "fisherman"]
widgets = {
"datetime": forms.DateTimeInput(format='%Y-%m-%d %H:%M', attrs={'class':'datetimefield form-control'}),
}
views.py
I' ve read that get_form_kwargs should be used in views to override fields in the form, but it didn't work for me.
class NewCatchView(CreateView):
model = Catch
form_class = CatchForm
template_name = "new_trip/new_catch.html"
# Probably, this is wrong
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['fisherman'] = Fisherman.objects.filter(trip=self.kwargs.get('pk'))
return kwargs
def form_valid(self, form):
form.instance.trip = Trips.objects.get(pk=self.kwargs['pk'])
return super().form_valid(form)
def get_success_url(self):
return reverse('new_trip:trip_details', args=(self.kwargs['pk'],))
urls.py
path("trip_details/<int:pk>/new_catch/", views.NewCatchView.as_view(), name="new_catch"),
Thank you in advance for your help!
You're almost there. You've created the kwarg, so now you just need to use it in the form to overwrite the original queryset:
class CatchForm(forms.ModelForm):
...
def __init__(self, *args, **kwargs):
fisherman = kwargs.pop('fisherman')
super().__init__(*args, **kwargs)
self.fields['fisherman'].queryset = fisherman

NOT NULL constraint failed: products_order.user_id when trying to save serializer django rest frame work

django rest framework gives me this error when i am trying to perform a post request to create a new order even though i specified the usr value in my serializer.save method.
here is my view
class OrderListCreateAPI(generics.ListCreateAPIView):
serializer_class = OrderSerializer
permission_classes = [IsOwner]
def get_queryset(self):
user = self.request.user
return Order.objects.filter(user= user)
def perform_update(self, serializer):
instance = serializer.save(user = self.request.user)
order = Order.objects.get(id = instance['id'].value)
order.item.quantity -= order.quantity
order.save()
my models.py
payment_methods = ((1,'credit card'),(2, 'cash'),(3, 'paypal'))
class Product(models.Model):
title = models.CharField(max_length=100)
description = models.TextField()
image = models.ImageField(upload_to = 'images/', height_field=300, width_field=300)
price = models.IntegerField()
quantity = models.IntegerField()
seller = models.ForeignKey(User, on_delete=models.CASCADE, related_name='products')
date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
class Order(models.Model):
item = models.ForeignKey(Product, on_delete= models.CASCADE, related_name='orders')
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='orders')
quantity = models.IntegerField()
payment_options = models.CharField(choices=payment_methods, max_length=50)
Delivery = models.CharField(max_length=200)
my serializers
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = ['id', 'title', 'description','image', 'price', 'quantity', 'seller', 'date']
read_only_fields = ['date', 'seller']
class OrderSerializer(serializers.ModelSerializer):
class Meta:
model = Order
fields = '__all__'
read_only_fields = ['user']
You need to use perform_create and not perform_update in your view.
class OrderListCreateAPI(generics.ListCreateAPIView):
serializer_class = OrderSerializer
permission_classes = [IsOwner]
def get_queryset(self):
user = self.request.user
return Order.objects.filter(user= user)
def perform_create(self, serializer):
instance = serializer.save(user = self.request.user)
order = Order.objects.get(id = instance['id'].value)
order.item.quantity -= order.quantity
order.save()

Fetch field Details for a login use in Django REST Framework with foreignkeyfield Relationship

I am trying to add Value into InstantInvestment Model in Django REST Framework which is working. but, only want to show the shipping that is specifically for the login user in. which means, the present situation is giving all the shipping not for this user.
models.py
class Shipping(models.Model):
investor = models.ForeignKey(User, related_name='shipping', on_delete=models.CASCADE)
beneficiary = models.CharField("Beneficiary Name", max_length=150)
bank = models.ForeignKey(Bank, related_name="bank", on_delete=models.CASCADE)
account = models.CharField(max_length=10)
address = models.TextField("Shipping Adresss")
created_at = models.DateTimeField(auto_now_add=True)
update_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.beneficiary
class Meta:
verbose_name = 'Shipping'
verbose_name_plural = 'Shippings'
class InstantInvestment(models.Model):
investor = models.ForeignKey(User, related_name='instantivestment', on_delete=models.CASCADE)
ref_code = models.CharField(max_length=200, blank=True, null=True)
investment = models.FloatField("Investment in dollar")
rate = models.FloatField("Exchange Rate")
transferable = models.FloatField("Money Transferable")
conversion = models.FloatField("Rate in Naira")
product = models.ForeignKey(Product, related_name='instant_product', on_delete=models.CASCADE)
shipping = models.ForeignKey(Shipping, related_name='shipping', on_delete=models.CASCADE)
done = models.BooleanField("Completed Transaction", default=False)
created_at = models.DateTimeField(auto_now_add=True)
update_at = models.DateTimeField(auto_now=True)
def __str__(self):
return f'{self.investor.get_full_name()} - Transaction Code: {self.ref_code}'
class Meta:
verbose_name = 'InstantInvestment'
verbose_name_plural = 'InstantInvestments'
serializers.py
class ShippingSerializer(serializers.ModelSerializer):
class Meta:
model = Shipping
fields = ('beneficiary', 'bank', 'account', 'address')
class QucikPaymentSerializer(serializers.ModelSerializer):
class Meta:
model = InstantInvestment
fields = ('url', 'id','investment', 'rate', 'transferable', 'conversion', 'product', 'shipping')
views.py
class QuickPaymentView(viewsets.ModelViewSet):
queryset = InstantInvestment.objects.all()
serializer_class = QucikPaymentSerializer
permission_classes = [ permissions.IsAuthenticated ]
def get_queryset(self):
return InstantInvestment.objects.filter(investor=self.request.user, done=False)
def perform_create(self, serializer):
serializer.save(investor=self.request.user)
Remove the query set attribute in your viewset class
class QuickPaymentView(viewsets.ModelViewSet):
serializer_class = QucikPaymentSerializer
permission_classes = [ permissions.IsAuthenticated ]
def get_queryset(self):
return InstantInvestment.objects.filter(investor=self.request.user, done=False)
def perform_create(self, serializer):
serializer.save(investor=self.request.user)
to make it work you need to specify the basename key word argument when you register your viewset class with router.
router.register(r'quickpayment/' , QuickPaymentView , basename='InstantInvestment')
If you want do so you need to write a html separately. I think that you showed in your question is rest frameworks ui to test the api. That UI can't determine the User before you send the request.

Django Restful: Nested Serializers

I'm adding a 'tests' field to my 'Sample' model, where 'tests' will be a list of 'TestRequest' objects. Currently, I'm getting this error:
Got AttributeError when attempting to get a value for field `tests` on serializer `SampleSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `Sample` instance.
Original exception text was: 'Sample' object has no attribute 'tests'.
'tests' is not a field on my model. I'm just trying to add it to the serialized data. Currently, I can get a nested serializer to work for 'klass' but that is because it's defined in the model.
Models:
class Sample(models.Model):
name = models.CharField(max_length=50, null=False, blank=False)
comments = models.TextField(null=True, blank=True)
klass = models.ForeignKey('samples.Batch', null=True, blank=True,
related_name='samples', verbose_name='Batch')
product = models.ForeignKey('customers.Product', blank=False)
NOTRECEIVED = 'nr'
RECEIVED = 'rc'
DISCARDED = 'dc'
DEPLETED = 'dp'
SAMPLE_STATUS = (
(NOTRECEIVED, 'not received'),
(RECEIVED, 'received'),
(DISCARDED, 'discarded'),
(DEPLETED, 'depleted'),
)
status = models.CharField(
max_length=2, choices=SAMPLE_STATUS, default=NOTRECEIVED)
is_recycling = models.BooleanField(default=False)
is_submitted = models.BooleanField(default=False)
received_date = models.DateTimeField(
_('date received'), null=True, blank=True)
class TestRequest(models.Model):
client = models.ForeignKey('customers.Client')
company = models.ForeignKey('customers.Company')
sample = models.ForeignKey('samples.Sample')
procedure_version = models.ForeignKey('registery.ProcedureVersion')
replicates = models.PositiveIntegerField(editable=True, null=True, blank=True)
created_date = models.DateTimeField('Date created', auto_now_add=True)
last_updated = models.DateTimeField(auto_now=True)
comments = models.TextField('Comments', blank=True)
Serializers:
class TestSerializer(serializers.ModelSerializer):
href = serializers.HyperlinkedIdentityField(lookup_field='pk', lookup_url_kwarg='pk', read_only=True, view_name='samples_api:test-detail')
class Meta:
model = TestRequest
fields = ('id', 'href',)
class SampleBatchSerializer(serializers.ModelSerializer):
href = serializers.HyperlinkedIdentityField(
lookup_field='pk', lookup_url_kwarg='batch_pk', read_only=True, view_name='samples_api:batch-detail')
class Meta:
model = Batch
fields = ('id', 'href',)
class SampleSerializer(serializers.ModelSerializer):
tests = TestSerializer(many=True)
klass = SampleBatchSerializer(many=False)
class Meta:
model = Sample
# list_serializer_class = FilteredListSerializer
fields = ('id', 'name', 'tests', 'klass',)
def create(self, validated_data):
...
def update(self, instance, validated_data):
...
Viewsets:
class TestRequestViewSet(viewsets.ModelViewSet):
"""
Viewset for the TestRequest model
"""
serializer_class = TestRequestSerializer
def get_queryset(self):
client = get_object_or_404(Client, user=self.request.user)
return TestRequest.objects.filter(company=client.company)
def perform_create(self, serializer):
# Override default creatation to provide request based information.
client = get_object_or_404(Client, user=self.request.user)
company = client.company
serializer.save(client=client, company=company)
class SampleViewSet(viewsets.ModelViewSet):
"""
Viewset for the Sample model
"""
serializer_class = SampleSerializer
def get_queryset(self):
client = get_object_or_404(Client, user=self.request.user)
return Sample.objects.filter(klass__company=client.company)
I would rather not have to add the field to the model. A 'Sample' can have many 'TestRequest's but a 'TestRequest' can only have one 'Sample'.
How do I get my serializer to add the 'tests' field that isn't in the model?
in your SampleSerializer. You have specified 'tests' which is not in your Sample class in your model...
Use nested SerializerMethodField as below....
tests = serializers.SerializerMethodField()
def get_tests(self, obj):
var=TestRequestSerializer(obj.id)
return var.data