How can I get user from serializer? - python

I am trying to override perform_update in my views, get user from serializer and assign it to updated_by in my Category model.
class Category(models.Model):
name = models.CharField(max_length=80)
created_at = models.DateTimeField(auto_now_add = True, null = True)
created_by = CurrentUserField()
updated_at = models.DateTimeField(auto_now = True, null = True)
updated_by = models.ForeignKey(User, default = None, blank=True, null=True, on_delete=models.DO_NOTHING, related_name="updated_by")
deleted_at = models.DateTimeField(null = True)
deleted_by = models.ForeignKey(User, default = None, blank=True, null=True, on_delete=models.DO_NOTHING, related_name='deleted_by')
class Meta:
verbose_name_plural = "Categories"
def __str__(self):
return self.name
def soft_delete(self, deleter):
self.deleted_by = deleter
self.deleted_at = timezone.now()
self.save()
serializer
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = "__all__"
views:
class CategoryView(viewsets.ModelViewSet):
serializer_class = CategorySerializer
queryset = Category.objects.all()
def get_permissions(self):
if self.request.method == "GET":
self.permission_classes = (AllowAny, )
else:
self.permission_classes = (IsAdminUser, )
return super(CategoryView, self).get_permissions()
def perform_update(self, serializer):
instance = self.get_object()
instance.updated_by = username
serializer.save()
def destroy(self, request, *args, **kwargs):
instance = self.get_object()
deleter = self.request.user
self.perform_destroy(instance, deleter)
return Response(status=status.HTTP_204_NO_CONTENT)
def perform_destroy(self, instance, deleter):
instance.soft_delete(deleter)

User is available from request, not serializer. You are already using self.request.user in destroy() and you can do the same thing in perform_update()
def perform_update(self, serializer):
serializer.save(updated_by=self.request.user)

Related

drf: serializers.ModelField > How can i get user_id appropriately?

I want to implement the 'user_id' to be automatically saved at the backend without receiving it from the client. (when create object!)
This is my code.
models.py
class User(AbstractUser):
username = None
email = models.EmailField(max_length=255, unique=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
objects = UserManager()
social_profile = models.URLField(null=True,blank=True)
realname = models.CharField(max_length=50, blank=True)
nickname = models.CharField(max_length=50, null=True, unique=True)
address = models.CharField(max_length=200, blank=True)
phone = models.CharField(max_length=100, blank=True)
def __str__(self):
return self.email
class Item(models.Model):
user_id = models.ForeignKey(User, related_name='item_sets', on_delete=models.CASCADE)
category_id = models.ForeignKey(Category, related_name='item_sets', on_delete=models.DO_NOTHING)
description = models.TextField()
feature = models.TextField()
product_defect = models.TextField()
size = models.CharField(max_length=6)
height = models.DecimalField(max_digits=4, decimal_places=1, default=0)
weight = models.DecimalField(max_digits=4, decimal_places=1, default=0)
condition = models.CharField(max_length=20)
price = models.IntegerField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
sold_out = models.BooleanField(default=False)
def __str__(self):
return self.description
view.py
class ItemViewSet(ModelViewSet):
queryset = Item.objects.all()
serializer_class = ItemSerializer
filter_backends = [SearchFilter, OrderingFilter]
search_fields = ['description'] # ?search=
ordering_fields = ['created_at'] # ?ordering=
ordering = ['-created_at']
authentication_classes = (JWTCookieAuthentication,)
# create
def create(self, request, *args, **kwargs):
city = request.data['city']
gu = request.data['gu']
dong = request.data['dong']
if city is not None and gu is not None and dong is not None:
location = Location.objects.get(
Q(city=city) & Q(gu=gu) & Q(dong=dong)
)
else:
return Response(
{"message": "주소정보를 모두 입력해주세요."},
status=status.HTTP_400_BAD_REQUEST
)
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
new_item = Item.objects.get(id=serializer.data['id'])
headers = self.get_success_headers(serializer.data)
LocationSet.objects.create(item_id=new_item, location_id=location)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
# here!
def perform_create(self, serializer):
serializer.save(user_id=self.request.user)
serializers.py
class ItemSerializer(serializers.ModelSerializer):
photos = PhotoSerializer(source='photo_sets', many=True, read_only=True)
style_photos = StylePhotoSerializer(source='style_photo_sets', many=True, read_only=True)
user_id = serializers.ModelField(model_field=User()._meta.get_field('id'), required=False) #here!
class Meta:
model = Item
fields = '__all__'
# Photo, StylePhoto
def create(self, validated_data):
images_data = self.context['request'].FILES
item = Item.objects.create(**validated_data)
for photo_data in images_data.getlist('photos_data'):
Photo.objects.create(item_id=item, photo=photo_data)
for style_photo_data in images_data.getlist('style_photos_data'):
StylePhoto.objects.create(item_id=item, user_id=self.context['request'].user,
photo=style_photo_data)
print(User()._meta.get_field('id'))
return item
so I overrided perform_create() in views.py
and write 'user_id = serializers.ModelField(model_field=User()._meta.get_field('id'), required=False)' in serializers.py
but it doesn't work well like my intend...
correct user_id is 8. but my object has 18..:<
18 is just item_id, not user_id..
what should I do to fix this error?
Try this:
views.py : use the perform_create() method to save the user_id.
Note: the user must be authenticated so that we can get them from the request.
class ItemViewSet(ModelViewSet):
# your code ...
# create
def create(self, request, *args, **kwargs):
# your code code ...
def perform_create(self, serializer): # new
serializer.save(user_id=self.request.user)
serializers.py :
Get the user_id from the database and make it readonly as it is not supposed to be edited by the user:
class ItemSerializer(serializers.ModelSerializer):
# your code ..
user_id = serializers.ReadOnlyField(source='user_id.id') # new
class Meta:
model = Item
fields = '__all__'
# Photo, StylePhoto
def create(self, validated_data):
images_data = self.context['request'].FILES
item = Item.objects.create(**validated_data)
for photo_data in images_data.getlist('photos_data'):
Photo.objects.create(item_id=item, photo=photo_data)
for style_photo_data in images_data.getlist('style_photos_data'):
StylePhoto.objects.create(item_id=item,photo=style_photo_data) # new
print(User()._meta.get_field('id'))
return item
I would also recommend you change the user_id field to something like owner to prevent future confusion.

Cannot successfully pass foreign_key reference to PUT request in Django REST

I am getting into Django REST and I am trying to use it for a backend of type of crypto currency tracker for my use. However I have been stuck on an issue for a few days..
I have the following models.py:
class Coin(models.Model):
sign = models.CharField(max_length=50, primary_key=True) # capitalize all entries
name = models.CharField(max_length=50, null=False)
amount = models.DecimalField(max_digits=20, decimal_places=2, null=False)
price_each = models.DecimalField(max_digits=20, decimal_places=2, null=False)
purchased_on = models.DateField(null=True)
def __str__(self):
return self.sign
def save(self, *args, **kwargs):
self.sign = self.sign.upper()
return super(Coin, self).save(*args, **kwargs)
class Price(models.Model):
coin = models.ForeignKey(Coin, related_name="prices", on_delete=models.CASCADE)
day_date = models.DateField(null=False)
current_price = models.DecimalField(max_digits=20, decimal_places=2, null=False)
def __str__(self):
timestamp = self.day_date.strftime("%d-%b-%Y")
return timestamp
and serializers.py:
class PriceSerializer(serializers.ModelSerializer):
coin_sign = serializers.PrimaryKeyRelatedField(queryset=Coin.objects.all(), source='coin.sign')
class Meta:
model = Price
fields = ['id', 'day_date', 'current_price', 'coin_sign']
def create(self, validated_data):
return Price.objects.create(**validated_data)
class CoinSerializer(serializers.ModelSerializer):
prices = PriceSerializer(many=True, read_only=False)
class Meta:
model = Coin
fields = ['sign', 'name', 'amount', 'price_each', 'purchased_on', 'prices']
def create(self, validated_data):
return Coin.objects.create(**validated_data)
I am having trouble defining a view with PUT to create a new price entry in view.py:
class AddPrice(APIView):
def put(self, request, sign, format=None):
coin = get_object_or_404(Coin, sign=sign.upper())
request.data['coin_sign'] = coin
serializer = PriceSerializer(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)
Here is the URL where I try to hit the PUT endpoint:
http://127.0.0.1:8000/crypto/coins/cro/add_price/
with following body:
{
"day_date": "2021-12-04",
"current_price": "420.69"
}
and the urlpattern from my_app/urls.py:
path('', views.index, name='index'), # http://127.0.0.1:8000/crypto/
path('coins/', views.Coins.as_view(), name='all_coins'),
path('coins/<str:sign>/', views.CoinInfo.as_view(), name='coin_info'),
path('coins/<str:sign>/add_price/', views.AddPrice.as_view(), name='add_price'),
I understand that I am passing a an object's representation of itself with that instead of the actual object and I cannot link the Price to the Coin properly because of that:
Cannot assign "{'sign': <Coin: CRO>}": "Price.coin" must be a "Coin" instance.
How do I create a Price entity from the endpoint successfully?
Thank you for reading!
I managed to create a resource with a POST request using my endpoint.
It is ugly and it does not use the serializer's validation and I probably would not recommend it.
models.py:
class Coin(models.Model):
sign = models.CharField(max_length=50, primary_key=True) # capitalize all entries
name = models.CharField(max_length=50, null=False)
amount = models.DecimalField(max_digits=20, decimal_places=2, null=False)
price_each = models.DecimalField(max_digits=20, decimal_places=2, null=False)
purchased_on = models.DateField(null=True)
# def __str__(self):
# return self.sign
def save(self, *args, **kwargs):
self.sign = self.sign.upper()
return super(Coin, self).save(*args, **kwargs)
class Price(models.Model):
coin = models.ForeignKey(Coin, related_name="prices", on_delete=models.CASCADE)
day_date = models.DateField(null=False)
current_price = models.DecimalField(max_digits=20, decimal_places=2, null=False)
serializers.py:
class PriceSerializer(serializers.ModelSerializer):
coin_sign = serializers.PrimaryKeyRelatedField(queryset=Coin.objects.all(), source='coin.sign')
class Meta:
model = Price
fields = ['id', 'day_date', 'current_price', 'coin_sign']
def create(self, validated_data):
return Price.objects.create(**validated_data)
class CoinSerializer(serializers.ModelSerializer):
prices = PriceSerializer(many=True, read_only=True)
class Meta:
model = Coin
fields = ['sign', 'name', 'amount', 'price_each', 'purchased_on', 'prices']
def create(self, validated_data):
return Coin.objects.create(**validated_data)
my view in vies.py:
class AddPrice(APIView): # not using serializer's validation, but will leave it as is for now
def post(self, request, sign):
coin = get_object_or_404(Coin, sign=sign.upper())
day_date = request.data.get('day_date')
current_price = request.data.get('current_price')
price = Price(coin=coin, day_date=day_date, current_price=current_price)
price.save()
serializer = PriceSerializer(price, context={'request': request})
return Response(serializer.data, status=status.HTTP_201_CREATED)

How to make a user profile view?

Since I am new to django, this is my first time trying to create a user profile. And I don't know how to make a profile view with a link to it. Could you please give me an example
models.py
class CustomUser(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(_('email address'), unique=True)
username = models.CharField(max_length = 40, unique=True)
is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
date_joined = models.DateTimeField(default=timezone.now)
slug = AutoSlugField(populate_from = 'username', null=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
objects = CustomUserManager()
class Meta:
verbose_name = 'Пользователь'
verbose_name_plural = 'Пользователи'
def __str__(self):
return self.email
def get_absolute_url(self):
return reverse('profile', args=[str(self.slug)])
class UserProfile(models.Model):
user = models.OneToOneField(CustomUser, on_delete = models.CASCADE, null = True, db_constraint=False)
avatar = ProcessedImageField(
upload_to = 'avatar',
format = "JPEG",
default = "avatar/empty_avatar.jpg",
processors = [ResizeToFit(250, 200)],
options = {'quality': 90},
blank = True,
null = True,
)
description = models.TextField(default = '', blank = True, null = True)
slug = AutoSlugField(populate_from = 'user', null=True)
class Meta:
verbose_name = 'Профиль'
verbose_name_plural = 'Профили'
#receiver(post_save, sender=CustomUser)
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
#receiver(post_save, sender=CustomUser)
def save_user_profile(sender, instance, **kwargs):
try:
instance.userprofile.save()
except ObjectDoesNotExist:
UserProfile.objects.create(user=instance)
forms.py
class CustomUserCreationForm(UserCreationForm):
class Meta:
model = get_user_model()
fields = ('email', 'username',)
class CustomUserChangeForm(UserChangeForm):
class Meta:
model = get_user_model()
fields = ('email', 'username',)
class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ('avatar', 'description',)
views.py
I want to create a link to an object using a slug or username so that it looks like "accounts/profile/username"
class UserProfileUpdate(generic.UpdateView, generic.FormView):
model = UserProfile
template_name = 'users/profile.html'
form_class = UserProfileForm
def form_valid(self, form):
response = form.save(commit = False)
response.save()
return super(UserProfileUpdate, self).form_valid(form)
def get_context_data(self, **kwargs):
context = super(UserProfileUpdate, self).get_context_data(**kwargs)
context['user'] = CustomUser.objects.get(slug = slug)
context['profile'] = UserProfile.objects.get(user = context['user'])
return context
I also want users to be able to view other users' profiles, not just thier own

How can I override Django Rest Framework viewset perform_create() method to set a default value to a field

I have a model called Product and two viewsets that manage the same model in different ways. Each one must set a different value for field product_type, which is set read_only in the serializer SuplementSerializer.
I trying to override perform_create() method and set the value for field product_type, but it always takes the default value.
This is my model:
class Product(models.Model):
ROOM = 'ROOM'
SUPLEMENT = 'SUPLEMENT'
PRODUCT_TYPE_CHOICES = (
(ROOM, _('Room')),
(SUPLEMENT, _('Suplement'))
)
hotel = models.ForeignKey(Hotel, on_delete=models.PROTECT, related_name='products', verbose_name=_('Hotel'))
name = models.CharField(max_length=100, verbose_name=_('Name'))
product_type = models.CharField(max_length=9, choices=PRODUCT_TYPE_CHOICES, default=ROOM, verbose_name=_('Product Type'))
room_type = models.ForeignKey(RoomType, null=True, blank=True, on_delete=models.PROTECT, related_name='products', verbose_name=_('Room Type'))
plan_type = models.ForeignKey(PlanType, null=True, blank=True, on_delete=models.PROTECT, related_name='products', verbose_name=_('Plan Type'))
content_type = models.ForeignKey(ContentType, null=True, blank=True, on_delete=models.PROTECT, related_name='rate_base_products', verbose_name=_('Rate Base'))
object_id = models.PositiveIntegerField(null=True, blank=True)
rate_base = GenericForeignKey('content_type', 'object_id')
class Meta:
verbose_name = _('Product')
verbose_name_plural = _('Products')
def __str__(self):
return "[{}][{}]{}".format(self.id, self.hotel, self.name)
def save(self, *vars, **kwargs):
self.full_clean()
return super().save(*vars, **kwargs)
def clean(self, *vars, **kwargs):
if self.content_type != None:
if self.content_type.model != 'ages' and self.content_type.model != 'roombase' and self.content_type.model != 'product':
raise CustomValidation(_("rate_base must be an instance of either 'Ages' or a 'RoomBase'"), 'rate_base', status.HTTP_400_BAD_REQUEST)
These are my viewsets:
class SuplementViewSet(viewsets.ModelViewSet):
permission_classes = (permissions.IsAuthenticated,)
queryset = models.Product.objects.filter(product_type=models.Product.SUPLEMENT)
serializer_class = serializers.SuplementSerializer
filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter,)
search_fields = ('hotel__name', 'name')
def perform_create(self, instance):
instance.product_type = models.Product.SUPLEMENT
instance.save()
class ProductViewSet(viewsets.ModelViewSet):
permission_classes = (permissions.IsAuthenticated,)
queryset = models.Product.objects.all()
serializer_class = serializers.ProductSerializer
filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter,)
search_fields = ('hotel__name', 'name')
filter_fields = ('hotel__name', 'name')
And this is my serializer:
class SuplementSerializer(serializers.ModelSerializer):
class Meta:
model = models.Product
fields = ('id', 'hotel', 'product_type', 'name')
read_only_fields = ('product_type',)
def __init__(self, *args, **kwargs):
exclude = kwargs.pop('exclude', None)
super(SuplementSerializer, self).__init__(*args, **kwargs)
if exclude is not None:
for field_name in exclude:
self.fields.pop(field_name)
I don't know why I can't set the value for field product_type.
Argument to perform_create() method is serializer instance not an object instance. You can set the field as below
def perform_create(self, serz):
serz.save(product_type=models.Product.SUPLEMENT)
See explanation here http://www.django-rest-framework.org/tutorial/6-viewsets-and-routers/#refactoring-to-use-viewsets

DRF: Database not updating

Following are my files. It is simply adding products based on subcategories and categories. Everything is working fine but the data is not being saved in the database. I am not sure where I am going wrong.
models.py
from django.db import models
class Category(models.Model):
category = models.CharField(max_length=200)
parent = models.ForeignKey('self', blank=True, null=True, related_name='children')
class Meta:
unique_together = ('parent' , 'category')
def __str__(self):
return self.category
class SubCategory(models.Model):
subcategory = models.CharField(max_length=200)
category = models.ForeignKey('Category', null=True, blank=True)
parent = models.ForeignKey('self', blank=True, null=True, related_name='subchilren')
class Meta:
unique_together = ('parent' , 'subcategory')
def __str__(self):
return self.subcategory
class Product(models.Model):
name = models.CharField(max_length=200)
category = models.ForeignKey('Category', null=True, blank=True)
subcategory = models.ForeignKey('SubCategory', null=True, blank=True)
def __str__(self):
return self.name
views.py
class AddProducts(APIView):
serializer_class = AddProductsSerializer
def post(self, request, format=None):
data = request.data
serializer = AddProductsSerializer(data=data)
if serializer.is_valid(raise_exception=True):
new_data = serializer.data
return Response(new_data)
return Response(serializer.errors, status=HTTP_400_BAD_REQUEST)
serializers.py
class AddProductsSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = ('id', 'name', 'category', 'subcategory')
def create(self, validated_data):
return Product.objects.create(**validated_data)
def update(self, instance, validated_data):
instance.name = validated_data.get('name', instance.name)
instance.category = validated_data.get('category', instance.category)
instance.subcategory = validated_data.get('subcategory', instance.subcategory)
instance.save()
return instance
if serializer.is_valid(raise_exception=True):
serializer.save()
new_data = serializer.data
return Response(new_data)
add serializer.save() will call serializer create method when serializer has no instance.

Categories

Resources