Convert function base view to class based view (DRF) - django

can someone help me convert this function component to class based view (rest framework concrete view)?
I tried converting but landed with errors where serializer is false.
Product image is required in the query.
class Product(models.Model):
title = models.CharField(max_length=200, unique=True)
slug = models.SlugField(max_length=200, unique=True)
description = models.TextField()
price = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True)
stock = models.IntegerField()
is_available = models.BooleanField(default=True)
brand = models.ForeignKey(Brand, on_delete=models.CASCADE)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
created_date = models.DateTimeField(auto_now_add=True)
updated_date = models.DateTimeField(auto_now=True)
class ProductImage(models.Model):
image = models.ImageField(upload_to=get_upload_path)
product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name="product_images")
def store(request, category_slug):
cat = None
products = None
if category_slug != None:
cat = get_object_or_404(Category, slug=category_slug)
products = Product.objects.filter(category=cat)
product_count = products.count()
else:
products = Product.objects.filter(is_available=True)
products.count()
context = {
'products': products,
'product_count': product_count
}
return render(request, 'store.html', context)
what i tried with rest framework.
Serializers:
class ProductImageSerializer(serializers.ModelSerializer):
class Meta: model = ProductImage
fields = ('image',)
class ProductSerializer(serializers.ModelSerializer):
product_images = ProductImageSerializer(many=True, read_only=True)
class Meta:
model = Product
fields = "__all__"
def create(self, validated_data):
profile_data = validated_data.pop('product_images')
product = Product.objects.create(**validated_data)
return product
View:
I tried with concrete views, product is filtered by category.
class Store(generics.ListCreateAPIView):
queryset = Product.objects.filter(is_available=True)
serializer_class = ProductSerializer
lookup_field = 'category_slug'
lookup_url_kwarg = 'category_slug'
def list(self, request, *args, **kwargs):
queryset = self.get_queryset()
cat_slug = self.kwargs.get('category_slug')
products = None
if cat_slug is not None:
category = get_object_or_404(Category, slug=cat_slug)
products = queryset.filter(category=category, is_available=True)
else:
products = queryset.filter(is_available=True)
if products.count() == 1:
serializer = ProductSerializer(data=products.first())
elif products.count() > 1:
serializer = ProductSerializer(data=products, many=True)
if not serializer.is_valid():
print(serializer.errors)
data = serializer.data
return Response(data, status=status.HTTP_200_OK)
any help is appreciated.

Related

Passing a placeholder text using django-filter and different views

I have a model:
class Movie (models.Model):
category = models.CharField(max_length=50, verbose_name='Kategoria filmu', default= 'unassigned', null=False, choices=category_choices)
source = models.CharField(max_length=50, verbose_name='Źródło filmu', default= 'unassigned', null=False, choices=source_choices)
promotion = models.BooleanField(default=False, verbose_name='PROMOCJA FILMU')
author = models.CharField(max_length=50, verbose_name='Nazwa influencera')
title = models.CharField(max_length=50, verbose_name='Nazwa filmu')
content = models.TextField(max_length=10000, verbose_name='HTML EMBEDED do filmu')
date_posted = models.DateTimeField(default=timezone.now)
youtube_url = models.URLField(blank=True, max_length=300)
tiktok_url = models.URLField(blank=True, max_length=300)
insta_url = models.URLField(blank=True, max_length=300)
I am passing it to the view with djnago-filter with different category choice:
views.py:
#HotTop View
class HotTopView (FilterView):
model = Movie
template_name = 'pages/hot_top.html'
filterset_class = MovieFilter
paginate_by = 6
def get_queryset(self):
category_qs = self.model.objects.filter(category="HOT-TOP")
return category_qs.order_by('-date_posted')
#Odkrycia View
class OdkryciaView (FilterView):
model = Movie
template_name = 'pages/odkrycia.html'
filterset_class = MovieFilter
paginate_by = 6
def get_queryset(self):
category_qs = self.model.objects.filter(category="ODKRYCIA")
return category_qs.order_by('-date_posted')
and my filters.py:
class MovieFilter(django_filters.FilterSet):
author = django_filters.CharFilter(label='', lookup_expr='contains', widget=TextInput(attrs={'placeholder': 'Search'}))
class Meta:
model = Movie
fields = ['author']
The question is how can i change placeholder of my serach form depending on the view (HotTop or Odkrycia). I want it to be - when i am in HotTop View -> Search in Hot Top and when i am in Odkrycia - > Search in Odkrycia
I think you could pass an argument
class MovieFilter(django_filters.FilterSet):
author = django_filters.CharFilter(label='', lookup_expr='contains', widget=TextInput(attrs={'placeholder': 'Search'}))
class Meta:
model = Movie
fields = ['author']
def __init__(self, *args, **kwargs):
placeholder = kwargs.pop('placeholder', None)
super().__init__(*args, **kwargs)
if placeholder :
self.fields['author'].widget = TextInput(attrs={'placeholder': f'Search in {placeholder}'})
and then in the views you can use get_filterset_kwargs method to pass the view name.
#HotTop View
class HotTopView(FilterView):
...
def get_filterset_kwargs(self):
kwargs = super(HotTopView, self).get_filterset_kwargs()
kwargs['placeholder'] = "Hot Top"
return kwargs
...
the other view:
#Odkrycia View
class OdkryciaView(FilterView):
...
def get_filterset_kwargs(self):
kwargs = super(OdkryciaView, self).get_filterset_kwargs()
kwargs['placeholder'] = "Odkrycia"
return kwargs
...
I hope that answers your question.

How can I show the StringRelatedField instead of the Primary Key while still being able to write-to that field using Django Rest Framework?

Models:
class CrewMember(models.Model):
DEPARTMENT_CHOICES = [
("deck", "Deck"),
("engineering", "Engineering"),
("interior", "Interior")
]
first_name = models.CharField(max_length=25)
last_name = models.CharField(max_length=25)
email = models.EmailField()
department = models.CharField(max_length=12, choices=DEPARTMENT_CHOICES)
date_of_birth = models.DateField()
join_date = models.DateField()
return_date = models.DateField(null=True, blank=True)
leave_date = models.DateField(null=True, blank=True)
avatar = models.ImageField(null=True, blank=True)
active = models.BooleanField(default=True)
def __str__(self):
return f"{self.first_name} {self.last_name}"
class RosterInstance(models.Model):
date = models.DateField(default=timezone.now)
deckhand_watchkeeper = models.ForeignKey(CrewMember, on_delete=models.PROTECT, null=True, related_name="deckhand_watches")
night_watchkeeper = models.ForeignKey(CrewMember, on_delete=models.PROTECT, null=True, related_name="night_watches")
def __str__(self):
return self.date.strftime("%d %b, %Y")
Views:
class CrewMemberViewSet(viewsets.ModelViewSet):
queryset = CrewMember.objects.all()
serializer_class = CrewMemberSerializer
filter_backends = [SearchFilter]
search_fields = ["department"]
def destroy(self, request, *args, **kwargs):
instance = self.get_object()
instance.active = False
instance.save()
return Response(status=status.HTTP_204_NO_CONTENT)
class RosterInstanceViewSet(viewsets.ModelViewSet):
queryset = RosterInstance.objects.all()
serializer_class = RosterInstanceSerializer
Serializers:
class CrewMemberSerializer(serializers.ModelSerializer):
class Meta:
model = CrewMember
fields = "__all__"
class RosterInstanceSerializer(serializers.ModelSerializer):
class Meta:
model = RosterInstance
fields = "__all__"
The resulting data looks like this:
{
"id": 2,
"date": "2020-12-09",
"deckhand_watchkeeper": 1,
"night_watchkeeper": 3
}
But I want it to look like this:
{
"id": 2,
"date": "2020-12-09",
"deckhand_watchkeeper": "Joe Soap",
"night_watchkeeper": "John Smith"
}
I can achieve the above output by using StringRelatedField in the RosterInstanceSerializer but then I can no longer add more instances to the RosterInstance model (I believe that is because StringRelatedField is read-only).
Because StringRelaredField is always read_only, you can use SlugRelatedField instead:
class RosterInstanceSerializer(serializers.ModelSerializer):
deckhand_watchkeeper = serializers.SlugRelatedField(
slug_field='deckhand_watchkeeper'
)
night_watchkeeper = serializers.SlugRelatedField(
slug_field='night_watchkeeper'
)
class Meta:
model = RosterInstance
fields = ['id', 'date', 'deckhand_watchkeeper', 'night_watchkeeper']
I was created a WritableStringRelatedField to do that.
class WritableStringRelatedField(serializers.SlugRelatedField):
def __init__(self, display_field=None, *args, **kwargs):
self.display_field = display_field
# Set what attribute to be represented.
# If `None`, use `Model.__str__()` .
super().__init__(*args, **kwargs)
def to_representation(self, obj):
# This function controls how to representation field.
if self.display_field:
return getattr(obj, self.display_field)
return str(obj)
def slug_representation(self, obj):
# It will be called by `get_choices()`.
return getattr(obj, self.slug_field)
def get_choices(self, cutoff=None):
queryset = self.get_queryset()
if queryset is None:
# Ensure that field.choices returns something sensible
# even when accessed with a read-only field.
return {}
if cutoff is not None:
queryset = queryset[:cutoff]
return OrderedDict([
(
self.slug_representation(item),
# Only this line has been overridden,
# the others are the same as `super().get_choices()`.
self.display_value(item)
)
for item in queryset
])
Serializers:
class RosterInstanceSerializer(serializers.ModelSerializer):
deckhand_watchkeeper = WritableStringRelatedField(
queryset=CrewMember.objects.all(),
slug_field='id',
label='Deckhand Watchkeeper',
)
night_watchkeeper = WritableStringRelatedField(
queryset=CrewMember.objects.all(),
slug_field='id',
label='Night Watchkeeper',
)
class Meta:
model = RosterInstance
fields = "__all__"

how to return several FileField from Queryset in Django?

I have a models with FileField for videos.
When i upload only one video i can return it with this code in the view.py:
def cours(request, id, slug):
c = Cours.objects.get(id=id, slug=slug)
p = Plan_simple.objects.get(cours=c)
return render(request, 'upload/cours.html', locals())
But when i upload two or more videos whith formset, and i change get fuction by filter it doesn't work:
def cours(request, id, slug):
c = Cours.objects.get(id=id, slug=slug)
p = Plan_simple.objects.filter(cours=c)
return render(request, 'upload/cours.html', locals())
the models.py
class Cours(models.Model):
titre = models.CharField(max_length=100)
slug = models.SlugField(max_length=100)
auteur = models.CharField(max_length=42)
comment = models.TextField(null=True)
link = models.CharField(max_length=100)
date = models.DateTimeField(default=timezone.now, verbose_name="Date de parution")
categorie = models.ForeignKey('Categorie', on_delete=models.CASCADE)
def save(self, *args, **kwargs):
self.slug = slugify(self.titre)
super(Cours, self).save(*args, **kwargs)
class Meta:
verbose_name = "cours"
db_table = "cours"
ordering = ['date']
class Plan_simple(models.Model):
partie = models.CharField(blank=True, max_length=100)
date = models.DateTimeField(default=timezone.now, verbose_name="Date de parution")
vid = models.FileField(upload_to='file/', blank=True, null = True)
cours = models.ForeignKey(Cours, related_name = "plan_simple", on_delete=models.CASCADE)
def __str__(self):
return self.partie
class Meta:
db_table = "plan_simple"
Can you help me?
Thanks
The issue is with this line. It does not request anything from the database. It only specifies a filter.
p = Plan_simple.objects.filter(cours=c)
You should change it to this...
p = Plan_simple.objects.filter(cours=c).all()

How to add default value in queryset?

I have a moneybook, moneylog models
and moneylog get relation with moneybook manytomany.
moneybook = models.ForeignKey(
moneybook_models.Moneybook, on_delete=models.CASCADE)
pay_day = models.DateTimeField(default=NOW)
payer = models.ForeignKey(
user_models.User, on_delete=models.CASCADE, related_name="payer")
dutch_payer = models.ManyToManyField(
user_models.User, related_name="dutch_payer", blank=True)
price = models.IntegerField()
category = models.CharField(max_length=10)
memo = models.TextField()
I want payer default save in dutch_payer if user choose or not.
and this is my forms.py
class CreateMoneylogForm(forms.ModelForm):
class Meta:
model = models.Moneylog
fields = (
"pay_day",
"payer",
"dutch_payer",
"price",
"category",
"memo",
)
widgets = {
"dutch_payer": forms.CheckboxSelectMultiple
}
def save(self, *args, **kwargs):
moneylog = super().save(commit=False)
return moneylog
views.py
class moneylog_create(FormView):
form_class = forms.CreateMoneylogForm
template_name = "moneylogs/create.html"
def form_valid(self, form):
moneylog = form.save()
moneybook = moneybook_models.Moneybook.objects.get(
pk=self.kwargs["pk"])
form.instance.moneybook = moneybook
if moneylog.payer not in moneylog.dutch_payer.all():
moneylog.dutch_payer.add(moneylog.payer)
moneylog.save()
form.save_m2m()
return redirect(reverse("cores:home"))
as you see.
if moneylog.payer not in moneylog.dutch_payer.all():
moneylog.dutch_payer.add(moneylog.payer)
moneylog.save()
form.save_m2m()
return redirect(reverse("cores:home"))
this not work.(there is no payer in dutch_payer) how can i achieve it?

Django category view

I am new to django and maybe this is a stupid question but i got stuck with this for a while now.. so i have a few categories of meds, like AINS, antidepressants and each of this category has its own meds, and i am trying to show my users all the meds of a specific category: so if a users types in www.namesite.com/meds/AINS the it will show only the meds for that specific category .. AINS.I think that i should get the absolute url of every category and filter all the meds in that specific category?
Model:
class Category(models.Model):
category = models.CharField(max_length=30)
slug = models.SlugField()
def __str__(self):
return self.category
def get_absolute_url(self):
return reverse("meds", kwargs={'slug':self.category})
class Meta:
verbose_name_plural = 'Categorii'
class Medicament(models.Model):
title = models.CharField(max_length=50)
description = models.TextField(max_length=200)
category = models.ForeignKey(Category, on_delete='CASCADE')
price = models.DecimalField(decimal_places=2, max_digits=4)
prospect = models.TextField(default='Prospect')
company = models.TextField(default = 'company')
nr_unitati = models.IntegerField()
quantity = models.CharField(max_length=5, default='mg')
date_added = models.DateTimeField(auto_now_add=True)
rating = models.IntegerField(null=True, blank=True)
amount = models.IntegerField(default=0)
def __str__(self):
return self.title + ' ' + self.company + ' ' + str(self.nr_unitati) + ' ' + self.quantity
class Meta:
verbose_name_plural = 'Medicamente'
Views:
class MedCategoriesView(DetailView):
model = Category
template_name = 'products/AINS.html'
context_object_name = 'all_categories'
def get_context_data(self, **kwargs):
context = super(AINS_ListView, self).get_context_data(**kwargs)
context['meds'] = Medicament.objects.filter(category=self.object)
return context
Urls:
path('medicaments/<slug>/', MedCategoriesView.as_view(), name='meds'),
Using function based views.
def medicament(request, slug):
try:
medicaments = Medicament.objects.filter(category__slug=slug)
except Medicament.DoesNotExist:
raise Http404("Medicament does not exist")
return render(request, 'products/AINS.html', {'medicaments': medicaments})