DRF is not updating the database - django

Here's the model:
from django.db import models
from datetime import datetime, timedelta
# Create your models here.
def set_expiration():
return datetime.today().date() + timedelta(days=30)
class Customer(models.Model):
email = models.EmailField(max_length=254, unique=True)
created_on = models.DateField(auto_now_add=True)
expires_on = models.DateField(editable=False, default=set_expiration())
def __str__(self):
return self.email
And this is the view:
#api_view(['POST'])
def update_customer(request):
try:
customer = Customer.objects.get(email=request.data['email'])
except ObjectDoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
serializer = CustomerSerializer(instance=customer, data=request.data)
if serializer.is_valid(raise_exception=True):
serializer.save()
return Response(serializer.data)
And the serilizer:
from rest_framework import serializers
from .models import Customer
class CustomerSerializer(serializers.ModelSerializer):
class Meta:
model = Customer
fields = '__all__'
At this moment I have a record in database with expires_on set to 2022-03-14 and I want to update that to 2024-12-12 so I call the endpoint with the following data:
{
"email": "myemail#mydomain.com",
"expires_on": "2024-12-12"
}
The view returns this:
{
"id": 1,
"email": "myemail#mydomain.com",
"created_on": "2022-02-12",
"expires_on": "2022-03-14"
}
This is the existing data. expires_on is not updated with the new value.
I get no error and no exception. It just doesn't work.

Remove editable=False from expires_on field.
class Customer(models.Model):
email = models.EmailField(max_length=254, unique=True)
created_on = models.DateField(auto_now_add=True)
expires_on = models.DateField(default=set_expiration())
def __str__(self):
return self.email

Related

Pytest-django - testing creation and passing a required User object

Apologies if this has already been answered elsewhere. I cannot find an answer which I can retrofit into my situation.
I'm new to django so I feel the problem is me not getting a fundamental grasp of a presumably basic concept here...
Using DRF and pytest-django, i'm trying to be diligent and write tests along the way before it becomes too time consuming to manually test. I can see it snowballing pretty quickly.
The issue I face is when I try to test the creation of a Catalogue, I can't get it to pass an User instance to the mandatory field 'created_by'. The logic works fine when I test manually, but writing the test itself is causing me headaches.
Many thanks in advance!
The error is:
TypeError: Cannot encode None for key 'created_by' as POST data. Did you mean to pass an empty string or omit the value?
Code provided.
# core/models.py
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
email = models.EmailField(unique=True)
# workshop/models.py
from django.conf import settings
from django.db import models
class Catalogue(models.Model):
STATE_DRAFT = 'DRAFT'
STATE_PUBLISHED_PRIVATE = 'PUBPRIV'
STATE_PUBLISHED_PUBLIC = 'PUBPUB'
STATE_CHOICES = [
(STATE_DRAFT, 'Draft'),
(STATE_PUBLISHED_PRIVATE, 'Published (Private)'),
(STATE_PUBLISHED_PUBLIC, 'Published (Public)')
]
company = models.ForeignKey(Company, on_delete=models.PROTECT)
title = models.CharField(max_length=255)
description = models.TextField(null=True, blank=True)
state = models.CharField(
max_length=10, choices=STATE_CHOICES, default=STATE_DRAFT)
created_by = models.ForeignKey(
settings.AUTH_USER_MODEL, on_delete=models.PROTECT)
created_on = models.DateTimeField(auto_now_add=True)
def __str__(self) -> str:
return self.title
class CatalogueItem(models.Model):
TYPE_GOODS = 'GOODS'
TYPE_SERVICES = 'SERVICES'
TYPE_GOODS_AND_SERVICES = 'GOODS_AND_SERVICES'
TYPE_CHOICES = [
(TYPE_GOODS, 'Goods'),
(TYPE_SERVICES, 'Services'),
(TYPE_GOODS_AND_SERVICES, 'Goods & Services')
]
catalogue = models.ForeignKey(
Catalogue, on_delete=models.CASCADE, related_name='catalogueitems')
type = models.CharField(
max_length=50, choices=TYPE_CHOICES, default=TYPE_GOODS)
name = models.CharField(max_length=255)
description = models.TextField()
unit_price = models.DecimalField(max_digits=9, decimal_places=2)
can_be_discounted = models.BooleanField(default=True)
def __str__(self) -> str:
return self.name
#property
def item_type(self):
return self.get_type_display()
# workshop/serializers.py
class CatalogueSerializer(serializers.ModelSerializer):
catalogueitems = SimpleCatalogueItemSerializer(
many=True, read_only=True)
created_on = serializers.DateTimeField(read_only=True)
created_by = serializers.PrimaryKeyRelatedField(read_only=True)
class Meta:
# depth = 1
model = Catalogue
fields = ['id', 'title', 'description',
'state', 'catalogueitems', 'created_by', 'created_on']
def create(self, validated_data):
company_id = self.context['company_id']
user = self.context['user']
return Catalogue.objects.create(company_id=company_id, created_by=user, **validated_data)
# workshop/views.py
class CatalogueViewSet(ModelViewSet):
serializer_class = CatalogueSerializer
def get_permissions(self):
if self.request.method in ['PATCH', 'PUT', 'DELETE', 'POST']:
return [IsAdminUser()]
return [IsAuthenticated()]
def get_queryset(self):
user = self.request.user
if user.is_staff:
return Catalogue.objects.prefetch_related('catalogueitems__catalogue').filter(company_id=self.kwargs['company_pk'])
elif user.is_authenticated:
return Catalogue.objects.filter(company_id=self.kwargs['company_pk'], state='PUBPUB')
def get_serializer_context(self):
company_id = self.kwargs['company_pk']
return {'company_id': company_id, 'user': self.request.user}
# workshop/tests/conftest.py
from core.models import User
from rest_framework.test import APIClient
import pytest
#pytest.fixture
def api_client():
return APIClient()
#pytest.fixture
def authenticate(api_client):
def do_authenticate(is_staff=False):
return api_client.force_authenticate(user=User(is_staff=is_staff))
return do_authenticate
# workshop/tests/test_catalogues.py
from core.models import User
from workshop.models import Catalogue
from rest_framework import status
import pytest
#pytest.fixture
def create_catalogue(api_client):
def do_create_catalogue(catalogue):
return api_client.post('/companies/1/catalogues/', catalogue)
return do_create_catalogue
class TestCreateCatalogue:
def test_if_admin_can_create_catalogue_returns_201(self, authenticate, create_catalogue):
user = authenticate(is_staff=True)
response = create_catalogue(
{'title': 'a', 'description': 'a', 'state': 'DRAFT','created_by':user})
assert response.status_code == status.HTTP_201_CREATED
I think you may have a problem with the user that you are using to do the test,
when you call authenticate it returns a client which is not the same as a user.
then you run the authenticate and log in as a generic user. Try making another fixture that creates a user first, authenticate with that user to return the client and then post that user you created to create_catalogue
from django.conf import settings
#pytest.fixture
def create_user() -> User:
return settings.AUTH_USER_MODEL.objects.create(
username="Test User", password="Test Password", email="testuser#example.com"
)
#pytest.fixture
def authenticate(api_client):
def do_authenticate(create_user):
return api_client.force_authenticate(create_user)
return do_authenticate
class TestCreateCatalogue:
def test_if_admin_can_create_catalogue_returns_201(self, authenticate, create_user create_catalogue):
user = authenticate(create_user)
response = create_catalogue(
{'title': 'a', 'description': 'a', 'state': 'DRAFT','created_by':create_user})
assert response.status_code == status.HTTP_201_CREATED

how to get tags from user in form and save it to database in django

I have a form that gets some fields and tags from users and save the user input data in database:
By the way I am using taggit
this is my model:
from taggit.managers import TaggableManager
class Question(models.Model):
title = models.CharField(max_length=500)
name = models.CharField(max_length=50, default=None)
slug = models.SlugField(max_length=500, unique_for_date='created', allow_unicode=True)
body = models.TextField(max_length=2000)
created = models.DateTimeField(auto_now_add=True)
tags = TaggableManager()
def get_absolute_url(self):
return reverse("questions:question_detail", args=[self.created.year, self.created.month, self.created.day, self.slug])
def __str__(self):
return self.title
and here is my view:
def question_form(request):
new_question = None
if request.method == 'POST':
question_form = QuestionForm(data=request.POST)
if question_form.is_valid():
new_question = question_form.save(commit=False)
new_question.slug = slugify(new_question.title)
new_question.save()
question_form.save_m2m()
else:
question_form = QuestionForm()
return render(request,
'questions/que/form.html',
{'question_form':question_form, 'new_question':new_question})
and my form.py is like this:
from taggit.forms import TagField
class QuestionForm(ModelForm):
class Meta:
model = Question
fields = ('name', 'title', 'body',)
tags = TagField()
my problem is when user enters the tags and other fields everything is saved in database except the tags! Can anyone help me?

Django Serializer Passing a arguments into model function

New to Django and DRF, I have a method in the model properties which accept arguments. I have managed to call it successful though a serializer class with default paramenters and getting a JSON response. My problem is I can't pass argument to that function named balance. I have successful pass my argument from view to serializer class but from serializer to model that where I have failed. I thought will be appreciated.
model.py
class Item(models.Model):
entered_by = models.ForeignKey(User, on_delete=models.PROTECT)
name = models.CharField(max_length=50, blank=True)
#property
def balance(self, stock_type='Retail'):
stock = Stock.objects.filter(item=self, type=stock_type, status='InStock').aggregate(models.Sum('quantity')).get('quantity__sum')
return stock or 0
views.py
def getItemInfo(request):
if request.is_ajax and request.method == "GET":
id = request.GET.get("item_id", None)
sell_type = request.GET.get("sell_type", None)
item = Item.objects.get(id=id)
if item:
serializer = ItemSerializer(item, context={'sell_type':sell_type})
return JsonResponse(serializer.data, status = 200, safe=False)
else:
return JsonResponse({"data":False}, status = 400)
serializer.py
from rest_framework import serializers
from .models import Item
class ItemSerializer(serializers.ModelSerializer):
balance = serializers.SerializerMethodField()
class Meta:
model = Item
fields = ('entered_by', 'name', 'balance', 'sell_mode')
def get_balance(self, object):
sell_type = self.context.get("sell_type")
if sell_type:
return object.balance(sell_type)
return object.balance
The error I'm getting
'int' object is not callable
#property couldn't be called. So I made member variable and setter (with calculation) methods in Item model, then make sure setter method will be called in get_balance serializer method, just before returning balance.
Django ORM model itself is just a class; you can do anything class could, not just only linking with ORM.
My Code:
models.py
from django.db import models
from django.contrib.auth.models import User
class Item(models.Model):
entered_by = models.ForeignKey(User, on_delete=models.PROTECT)
name = models.CharField(max_length=50, blank=True)
_balance = 0
def calculate_balance(self, stock_type='Retail'):
stock = Stock.objects.filter(item=self, type=stock_type, status='InStock').aggregate(
models.Sum('quantity')).get('quantity__sum')
self._balance = stock or 0
#property
def balance(self):
return self._balance
serializer.py
from django.contrib.auth.models import User
from rest_framework import serializers
from .models import Item
class ItemSerializer(serializers.ModelSerializer):
balance = serializers.SerializerMethodField()
class Meta:
model = Item
fields = ('entered_by', 'name', 'balance')
def get_balance(self, object):
sell_type = self.context.get("sell_type")
if sell_type:
object.calculate_balance(sell_type)
return object.balance
views.py
from .models import Item
from .serializer import ItemSerializer
from django.http.response import JsonResponse
def getItemInfo(request):
if request.is_ajax and request.method == "GET":
id = request.GET.get("item_id", None)
if id is None:
return JsonResponse({"data": False}, status=400)
sell_type = request.GET.get("sell_type", None)
try:
item = Item.objects.get(id=id)
serializer = ItemSerializer(item, context={'sell_type': sell_type})
return JsonResponse(serializer.data, status=200, safe=False)
except Item.DoesNotExist:
return JsonResponse({"data": False}, status=400)

The response content must be rendered before it can be iterated over. Django Rest Framework

I have a Cart model and Cartserializers. I am trying to do that is if cart defects exist in the cart and then update the cart by increasing the quantity of cart. I tried a lot to do this. But it raises an error every time this time is The response content must be rendered before it can be iterated over.
Here is my code :)
views.py*
class CartViewSet(viewsets.ModelViewSet):
serializer_class = CartSerializer
permission_classes = (IsAuthenticated,)
def get_queryset(self):
user = self.request.user
if user.is_authenticated:
if user is not None:
if user.is_active and user.is_superuser or user.is_Customer:
return Cart.objects.all()
raise PermissionDenied()
raise PermissionDenied()
raise PermissionDenied()
filter_backends = [DjangoFilterBackend]
filterset_fields = ['date_created', 'user']
#action(detail=False)
def count(self, request):
queryset = self.filter_queryset(self.get_queryset())
count = queryset.count()
content = {'count': count}
return Response(content)
def create(validated_data, get):
quantity, created = Cart.objects.update_or_create(
user = validated_data.get('user', None),
defects=validated_data.get('defects', None),
defaults={'quantity': validated_data.get('quantity' + str(1), None)})
return quantity
if quantity is created.create:
return Response ({
'status' : True,
"detail" : "created"
})
if quantity is created.update:
return Response ({
'status' : True,
"detail" : "updated"
})
models.py
from django.db import models
from accounts.models import User, SubCategory
# Create your models here.
class Cart(models.Model):
user = models.ForeignKey('accounts.User', related_name="carts", null=True, on_delete=models.SET_NULL)
quantity = models.IntegerField(default=1)
service = models.ForeignKey('accounts.SubCategory',null=True, on_delete=models.SET_NULL)
defects = models.ForeignKey('Defects',null=True, on_delete=models.SET_NULL)
price = models.IntegerField(default=False)
date_created = models.DateTimeField(auto_now_add=True)
total = models.IntegerField(blank=True, null=True)
def __str__(self):
return self.user.username
serializers.py
from rest_framework import serializers
class CartSerializer(serializers.ModelSerializer):
class Meta:
model = Cart
fields = ['id','url', 'user', 'service', 'defects', 'date_created', 'quantity' , 'price', 'total']
Your get_queryset() method must return a queryset!

Django rest framework sending a string with single quotes back instead of JSON with double quotes

I am sending JSON via POST to a view. The response I get back is only part JSON and the rest has changed from double quotes to single quotes.
My question is how to I make sure that I am sending all this back as JSON in a get? As an added bonus to my problem, I don't know what fields will be sent to leasee.
The POST looks like:
{"leasee":
{"profession":{"permanentMakeup":true,"esthetician":true,"hairStylist":true},
"compare":{"uniqueVsChic":"56"},
"name":"dfasdfasdf",
"contact":{"text":true,"facebook":true}}}
But GET gives me:
{"leasee":
"{'compare': {'uniqueVsChic': '56'},
'profession': {'hairStylist':True,
'esthetician': True,
'permanentMakeup': True},
'name':'dfasdfasdf',
'contact':{'facebook': True,'text': True}}",
"created_at":"2015-05-22T23:27:13.598686Z",
"updated_at":"2015-05-22T23:27:13.609893Z"}
my views.py:
from rest_framework import viewsets
from sitepages.models import SalonSubmission, StylistSubmission
from sitepages.serializers import SalonSerializer, StylistSerializer
from django.db.models.signals import post_save
from django.core.mail import send_mail
from django.dispatch import receiver
class SalonViewSet(viewsets.ModelViewSet):
queryset = SalonSubmission.objects.order_by('-created_at')
serializer_class = SalonSerializer
def perform_create(self, serializer):
instance = serializer.save()
post_save.send(sender=self.__class__, name=instance.business_name, phone=instance.phone)
return super(SalonViewSet, self).perform_create(serializer)
class StylistViewSet(viewsets.ModelViewSet):
queryset = StylistSubmission.objects.order_by('-created_at')
serializer_class = StylistSerializer
def perform_create(self, serializer):
instance = serializer.save()
post_save.send(sender=self.__class__, stylist=instance.leasee)
return super(StylistViewSet, self).perform_create(serializer)
#receiver(post_save, sender=SalonViewSet)
def send_email(sender, **kwargs):
send_mail(kwargs['name'], kwargs['phone'], 'from#example.com',
['to#example.com'], fail_silently=False)
#receiver(post_save, sender=StylistViewSet)
def send_email(sender, **kwargs):
send_mail('aName', kwargs['stylist'], 'from#example.com',
['to#example.com'], fail_silently=False)
My serializers.py:
from rest_framework import serializers
from sitepages.models import SalonSubmission, StylistSubmission
class SalonSerializer(serializers.ModelSerializer):
class Meta:
model = SalonSubmission
fields = ('business_name', 'phone', 'created_at', 'updated_at')
read_only_fields = ('created_at', 'updated_at')
class StylistSerializer(serializers.ModelSerializer):
class Meta:
model = StylistSubmission
fields = (
'leasee',
'created_at',
'updated_at'
)
read_only_fields = ('created_at', 'updated_at')
My models.py:
from django.db import models
class SalonSubmission(models.Model):
business_name = models.CharField(max_length=250)
phone = models.CharField(max_length=250)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class StylistSubmission(models.Model):
leasee = models.CharField(max_length=2000)
file = models.FileField(upload_to="/media/")
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
It appears that leasee = models.CharField(max_length=2000) is a charfield. So Django is simply returning the string stored in this field. Also it is not storing valid JSON string neither because JSON only allows double quote.
{'compare': {'uniqueVsChic': '56'},
'profession': {'hairStylist':True,
'esthetician': True,
'permanentMakeup': True},
'name':'dfasdfasdf',
'contact':{'facebook': True,'text': True}}
looks like stringified dict.
So you can try to parse leasee as a dict in your serializer.