I add post in admin panel, when i need to create qr cod from post.title text
i tried use django-qr-code library and ContactDetail model,but i get error:type object argument after ** must be a mapping, not Post
views.py
def index(request):
# Use a ContactDetail instance to encapsulate the detail of the contact.
posts = Post.objects.all()
contact_detail = ContactDetail(
first_name=Post.objects.get())
# Use a WifiConfig instance to encapsulate the configuration of the connexion.
# Build context for rendering QR codes.
context = dict(
posts=posts,
contact_detail=contact_detail,
)
# Render the index page.
return render(request, 'blog/post_list.html', context=context)
models.py
class Post(models.Model):
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
title = models.CharField(max_length=400)
html
{% qr_for_contact contact_detail=contact_detail size='S' %}
Related
I am creating an ecommerce website using Django-rest-framework and react. I am trying to add items to cart. I am able to add items to the cart on the frontend but I want to store the cart data to the backend (Django) database so that whenever the user add items to cart and reloads the page the items should still be in his cart like any other ecommerce site. Here is my Code for django models, serializers, viewset.
class Products(models.Model):
title = models.CharField(max_length=100)
image = models.URLField()
description = models.TextField(max_length=500, blank=True, null=True)
category = models.CharField(max_length=50, blank=True, null=True)
rating = models.IntegerField(blank=True, null=True)
price = models.FloatField()
def __str__(self):
return f"{self.id}"
class Cart(models.Model):
product = models.ManyToManyField(
Products, related_name="cart")
buyer = models.ForeignKey(
User, on_delete=models.CASCADE, related_name="cart")
def __str__(self) -> str:
return f"{self.buyer}"
class ProductsSerializer(serializers.ModelSerializer):
class Meta:
model = Products
fields = '__all__'
class CartSerializer(serializers.ModelSerializer):
class Meta:
model = Cart
fields = '__all__'
class ProductsViewSet(viewsets.ModelViewSet):
queryset = Products.objects.all()
serializer_class = ProductsSerializer
class CartViewSet(viewsets.ModelViewSet):
queryset = Cart.objects.all()
authentication_classes = [JWTAuthentication]
permission_classes = [
permissions.IsAuthenticated
]
serializer_class = CartSerializer
router = routers.DefaultRouter()
router.register('products', ProductsViewSet, 'products')
router.register('cart', CartViewSet, 'cart')
I am using postman to post the cart items. I am able to add more than one products for a single buyer.
but the problem is when i again add another product to the same user using postman i added before, it creates another row for the same user.
I do not want that. I want a single instance or row of a user in cart table and add as many products as i want. when i post other products for the same user, that product should also get added up in the single user row or instance. What is the best way to achieve my goal.
Here is the issue, Django can't automatically do that because it doesn't know which behaviour is expected. If you look at the code to add a product and look at the code to add a cart, it's exactly the same. So behaviour will also be the same.
For the behaviour that you want, you will have to override the create method of your ModelViewSet.
Here are the steps to achieve what you want -
Check whether or not the user with that id already has a cart.
If they have a cart, then you'll need to fetch the cart object belonging to that user and add products to it.
If they don't, then you'll have to create a new cart object and do the default thing.
class CartViewSet(viewsets.ModelViewSet):
queryset = Cart.objects.all()
authentication_classes = [JWTAuthentication]
permission_classes = [
permissions.IsAuthenticated
]
serializer_class = CartSerializer
def create(self, request):
# Checking whether the Cart object already exists for the buyer
cart = Cart.objects.filter(buyer_id = request.data.get("buyer"))
if len(cart)=1:
#If cart exists, we loop through the list of product ids and add them
for product_id in request.data.get("product"):
cart[0].product.add(get_object_or_404(Product, id = product_id ))
if len(cart)=0:
# Doing what normally happens.
return super().create(request)
if len(cart)>1:
#Error in database. One person having multiple carts. Custom error message.
To check out how to add data to many-to-many fields, check this out.
I've been trying to post a nested object with one file and some data via django drf for a few days now. The goal is to create a story together with some images, one of these images being the title image of the story. Basically, I am able to post the story successfully with postman (see picture below). However, when I use my react js frontend for sending, I am not able to create the form data in a way that Django understands it. Django always returns the error story_media field is required. I suppose this is because Django does not understand the incoming data correctly.
class Story (models.Model):
title = models.CharField(max_length=100,blank=True)
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=True)
class Story_Media (models.Model):
story = models.ForeignKey(Story,on_delete=models.CASCADE, null=True, related_name = 'story_media', related_query_name = 'story_media')
file = models.FileField(upload_to='story_media/', null=True)
isTitlePicture = models.BooleanField(blank=False, null=True)
# Story Story Media Serializer
class Story_Media_Serializer (serializers.ModelSerializer):
class Meta:
model = Story_Media
fields = ('id','file','isTitlePicture',)
# Story Serializer
class StoryCreateUpdateSerializer (serializers.ModelSerializer):
story_media = Story_Media_Serializer(many=True)
class Meta:
model = Story
fields = ('title','story_media',)
def create(self, validated_data):
current_user = self.context["request"].user
story_media = validated_data.pop('story_media')
story_instance = Story.objects.create(author=current_user, **validated_data)
for story_media_data in story_media:
Story_Media.objects.create(**story_media_data, story=story_instance)
# Story Create View Set
class StoryCreateUpdateViewSet(viewsets.ModelViewSet):
serializer_class = StoryCreateUpdateSerializer
queryset = Story.objects.all()
This is how i create my form data in react js. In this example story_media_array contains only a single image object.
// array that stores all the images
let story_media_array = [];
// single image object
var image_object ={
file: this.state.file[0], // some file
isTitlePicture: "True"
}
// push image object in array
story_media.push(image_object);
let formdata = new FormData();
// title
formdata.append('title', "Test")
// image
formData.append('story_media', story_media_array)
However, as I wrote above, I am not able to create the form with the above code, it returns 'story_media field is required'. But it works with postman. I'm a bit lost at the moment, so I'm glad for any help.
class User(models.Model):
name = models.EmailFiled()
class Product(models.Model):
title = models.CharField(max_length=50)
is_active = models.BooleanField(default=True)
class Cart(models.Model):
product = models.ManyToManyField(Product)
class Order(models.Model):
user = models.ForeignKey(User,on_delete=models.CASCADE)
cart = models.ForeignKey(Cart,on_delete=models.CASCADE)
We have multiple products. some of them are active=False the other ones are True. I should take the products which is active=true requesting user.
You are looking for the ability to filter results of a ViewSet based on your active flag. I highly recommend you to read DRF Filtering documentation
You simply need to add to your ViewSet or APIView the following fields
from django_filters.rest_framework import DjangoFilterBackend
class ProductList(generics.ListAPIView):
queryset = Product.objects.all()
serializer_class = ProductSerializer
# Add Filtering Backend
filter_backends = [DjangoFilterBackend]
# Add filtering fields (Default behavior is to exact match provided values)
filterset_fields = ['is_active']
and now in your request, you should have the query param
http://example.com/api/products?is_active=1
You can obtain the Products that are active and in the Cart of a user with:
Product.objects.filter(is_active=True, cart__order__user=my_user)
I am looking for a way to show a list of wagtail collection as a field in a page (just like it showing when you upload an image). A user can select a collection and I can programmatically filter the images to the selected collection. I am still new to wagtail and I am not sure how should I implement this in code.
Thank you in advance for your help.
So there's a couple ways you can do this. The first, and probably the least-ideal way is to register Collection as a snippet and use a SnippetChooserPanel.
"""Register Collection snippet."""
from wagtail.snippets.models import register_snippet
from wagtail.core.models import Collection
# Register Collections as Snippets so we can use the SnippetChooserPanel to select a collection
register_snippet(Collection)
And then in your model you can use a SnippetChooserPanel, like so (note, this is all untested code)
from django.db import models
from wagtail.core.models import Page
class CustomPage(Page):
# ...
collection = models.ForeignKey(
'wagtailcore.Collection',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+',
)
content_panels = Page.content_panels + [
# ...
SnippetChooserPanel('collection'),
]
#gasman's comment on the answer has a link to another solution that's much more elegant than mine.
I've managed to do this using wagtail-generic-chooser just following the instructions on the README.md, and using wagtail core Collection model instead of People.
Aug 2022 - Wagtail 2.15.5 - display Wagtail hierarchical collection
from wagtail.admin.templatetags.wagtailadmin_tags import format_collection
class Meeting(models.Model):
COLLECTION_CHOICES = []
for c in Collection.objects.all():
COLLECTION_CHOICES.append((c.id, format_collection(c)))
title = models.CharField(max_length=100)
collection = models.ForeignKey(Collection, on_delete=models.PROTECT, help_text="Choose the 'Collection' folder for the meeting's related documents", choices=COLLECTION_CHOICES)
Edit: If you add a new collection to collections and go back the this Meeting model the new collection will not be in the list. As the COLLECTION_CHOICES is only created once for optimization. If you want a dynamic collection choice you need to make a custom form on top of your model e.g.
from wagtail.admin.forms import WagtailAdminModelForm
class MeetingAdminForm(WagtailAdminModelForm):
# This below field will be automatically added to the Meeting panel fields
meeting_collection = forms.ChoiceField()
def __init__(self, *args, **kwargs):
super(MeetingAdminForm, self).__init__(*args, **kwargs)
self.fields['meeting_collection'] = forms.ChoiceField(
initial=self.instance.collection_id,
choices=[(c.id, format_collection(c)) for c in Collection.objects.all()]
)
def save(self, commit=True):
instance = super().save(commit=False)
instance.collection_id = self.cleaned_data['meeting_collection']
if commit:
instance.save()
return instance
class Meeting(models.Model):
base_form_class = MeetingAdminForm
class Meta:
""" Meta options """
ordering = ['title']
title = models.CharField(max_length=100)
meeting_datetime = models.DateTimeField()
location = models.TextField(null=True)
collection = models.ForeignKey(Collection, on_delete=models.PROTECT, help_text="Choose the 'Collection' folder for the meeting's agenda, minutes and related documents")
committee = models.ForeignKey(Committee, on_delete=models.CASCADE)
panels = [
FieldPanel('title'),
FieldPanel('meeting_datetime'),
FieldPanel('location'),
FieldPanel('meeting_collection'),
FieldPanel('committee'),
]
This question or many like it has been asked multiple times but for some reason I am unable to find the answer.
I do have this working to an extent in the way that if you go on the api pages, it renders, creates and updates without problem. The issue is displaying a field (title) from the nested object instead of just the primary key on the front end.
Some background before getting into the code:
Races is a finite list (e.g. Race1, Race2, Race3) and the front end does not have the ability to add more.
Cards is not finite, but each card must link to an existing Race (this currently does so by Primary Key).
The front end should display the card_text and race title of the linked race.
It also has the ability to add a new card but this works fine.
I have had this working with separate serializers for read and create/update where the read has a 'depth = 1' to pull through the entire object but the create/update doesn't and you then parse the object and send the primary key back (I couldn't find a way of doing this in the serializer, is it possible?).
So basically my question is, are you meant to pass the entire object through and parse it on a POST method, or do you pass the primary key and pull in the linked objects (Races) and use the primary key as an index (e.g. Races[card_race]). Also, why is 'linked_race' not coming through to the front end?
I realise I've almost answered my own question but as I'm new to Django I'm looking for the correct conventions and who knows, it may save someone else time when searching for the same answer.
urls.py
from .api import CardViewSet, RaceViewSet
from rest_framework.routers import DefaultRouter
from django.conf.urls import url, include
from .views import landing
router = DefaultRouter()
router.register(r'cards', CardViewSet)
router.register(r'races', RaceViewSet)
urlpatterns = [
url(r'^$', landing),
url(r'^api/', include(router.urls)),
]
api.py
from rest_framework.viewsets import ModelViewSet
from .serializers import CardSerializer, RaceSerializer
from .models import Card, Race
class CardViewSet(ModelViewSet):
queryset = Card.objects.filter(active=True)
def get_serializer_class(self):
return CardSerializer
def perform_create(self, serializer):
serializer.save(creator=self.request.user)
class RaceViewSet(ModelViewSet):
queryset = Race.objects.filter(active=True)
serializer_class = RaceSerializer
models.py
from django.db import models
from django.conf import settings
User = settings.AUTH_USER_MODEL
class Race(models.Model):
id = models.IntegerField(primary_key=True)
title = models.CharField(max_length=30, blank=False)
active = models.BooleanField(default=True)
def __str__(self):
return "{}".format(self.title)
def __unicode__(self):
return self.title
class Card(models.Model):
card_text = models.CharField(max_length=100, blank=False)
card_description = models.CharField(max_length=100, blank=True)
card_race = models.ForeignKey(Race, related_name='linked_race', on_delete=models.CASCADE)
creator = models.ForeignKey('auth.User', on_delete=models.CASCADE)
created = models.DateTimeField(auto_now_add=True)
active = models.BooleanField(default=True)
def __str__(self):
return self.card_text
class Meta:
ordering = ('created',)
serializers.py
from rest_framework import serializers
from .models import Card, Race
class RaceSerializer(serializers.ModelSerializer):
class Meta:
model = Race
fields = '__all__'
class CardSerializer(serializers.ModelSerializer):
linked_race = RaceSerializer(read_only=True, many=True)
class Meta:
model = Card
fields = 'id', 'card_text', 'card_description', 'card_race', 'linked_race',
Javascript extract (AngularJS)
$http.get('/api/races/').then(function (response) {
$scope.races = response.data;
$scope.selectedOption = $scope.races[0];
});
$scope.cards = [];
$http.get('/api/cards/').then(function (response) {
$scope.cards = orderBy(response.data, 'created', true);
});
html extract (AngularJS)
<div class="races--row" ng-repeat="c in cards | filter : card_filter |
orderBy : sortVal : sortDir" ng-class-odd="'odd'" ng-click="openModal(c)">
<div class="races--cell race">{{ c.card_race.title }}</div>
<div class="races--cell card-text">{{ c.card_text }}</div>
</div>
Your first "problem" is with the Card model (I say problem because I don't think you intended to do this). You're defining related_name='linked_race' for the card_race field. This related_name is the name you use to refer to a card FROM a race.
I would suggest you leave it out and use the default that Django already gives us (i.e. my_race.card_set.all() in this case). So change change that field in the Card model to:
class Card(models.Model):
...
card_race = models.ForeignKey(Race, on_delete=models.CASCADE)
...
And let's change the card serializer to:
class CardSerializer(serializers.ModelSerializer):
# no more linked_race
class Meta:
model = Card
fields = ('id', 'card_text', 'card_description', 'card_race')
Alright, this is a vary basic model serializer and you won't see details of a race yet. So now let's get to your main problem which was that you wanted to:
see the details of the associated race of a card
perform create/get/update/delete operations using the same serializer
For this, let's further change the CardSerializer to include another field called race_detail:
class CardSerializer(serializers.ModelSerializer):
race_detail = RaceSerializer(source='card_race', read_only=True)
class Meta:
model = Card
fields = ('id', 'card_text', 'card_description', 'card_race', 'race_detail')
We have defined two serializer fields for the same model field. Note the source and read_only attributes. This makes this field available when you GET a card (which is what we want), but not when you're performing POSTs or PUTs (which avoids the problem of sending the whole race object and parsing and stuff). You can just send the race id for the card_race field and it should work.