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
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.
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)
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
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
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.