private and public field in Django REST Framework - python

I want to have a list of public and private recipes, hiding all private recipes unless it's owner's. I created a manager for this:
class RecipeManager(models.Manager):
def public_recipes(self, *args, **kwargs):
return super(RecipeManager, self).filter(private=False)
def private_recipes(self, *args, **kwargs):
user = kwargs.pop('user')
return super(RecipeManager, self).filter(private=True, user=user)
class Recipe(models.Model):
name = models.CharField(max_length=100)
recipe = models.CharField(max_length=200)
private = models.BooleanField(default=False)
views.py:
class RecipeViewSet(viewsets.ModelViewSet):
queryset = Recipe.objects.all()
serializer_class = RecipeSerializer
permission_classes = (AllowAny,)
serializers.py:
class RecipeSerializer(serializers.ModelSerializer):
class Meta:
model = Recipe
fields = ('id', 'name', 'recipe', 'total_ingredients')
depth = 1
So, where can I use the methods public_recipes, private_recipes or is there a better solution for this?

Firstly, you may want to set your custom manager as the default manager of your Recipe model, like so:
class RecipeManager(models.Manager):
def public_recipes(self, *args, **kwargs):
return super(RecipeManager, self).filter(private=False)
def private_recipes(self, *args, **kwargs):
user = kwargs.pop('user')
return super(RecipeManager, self).filter(private=True, user=user)
class Recipe(models.Model):
name = models.CharField(max_length=100)
recipe = models.CharField(max_length=200)
private = models.BooleanField(default=False)
objects = RecipeManager() # Make this manager the default manager
You may override the get_queryset() method on your view to merge the private and public recipes for a user:
class RecipeViewSet(viewsets.ModelViewSet):
queryset = Recipe.objects.all()
serializer_class = RecipeSerializer
permission_classes = (AllowAny,)
def get_queryset(self):
if self.request.user:
private_recipes = Recipe.objects.private_recipes(user=self.request.user)
else:
private_recipes = Recipe.objects.none()
public_recipes = Recipe.objects.public_recipes()
final_recipes_list = private_recipes | public_recipes # Shorthand to merge two querysets
return final_recipes_list
I would actually recommend considering having different ViewSet's for public and private recipes, i.e.
class PublicRecipeViewSet(viewsets.ModelViewSet):
queryset = Recipe.objects.public_recipes()
class PrivateRecipeViewSet(viewsets.ModelViewSet):
queryset = Recipe.objects.filter(private=True)
def get_queryset(self):
if not self.request.user:
raise AuthenticationFailed()
queryset = super().get_queryset()
return queryset.filter(user=self.request.user)

Related

How to override the update action in django rest framework ModelViewSet?

These are the demo models
class Author(models.Model):
name = models.CharField(max_lenght=5)
class Post(models.Model):
author = models.ForeignKey(Author, on_delete=models.CASCADE)
title = models.CharField(max_lenght=50)
body = models.TextField()
And the respective views are
class AuthorViewSet(viewsets.ModelViewSet):
queryset = Author.objects.all()
serializer_class = AuthorSerializer
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostStatSerializer
I am trying to perform an update/put action on PostViewSet and which is succesfull, but I am expecting different output. After successful update of Post record, I want to send its Author record as output with AuthorSerializer. How to override this and add this functionality?
You can override update method for this:
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostStatSerializer
def update(self, request, *args, **kwargs):
partial = kwargs.pop('partial', False)
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=partial)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
# this will return autor's data as a response
return Response(AuthorSerializer(instance.parent).data)
I figured out some less code fix for my issue.
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostStatSerializer
def update(self, request, *args, **kwargs):
super().update(request, *args, **kwargs)
instance = self.get_object()
return Response(AuthorSerializer(instance.author).data)

How to fetch with multiple query parameters Genereic API view

so I am building an API and i want to fetch based on multiple parameters.
Here is the code base.
The Url path:
path('<str:order_id>/consumers/', SingleConsumerTradeAPIView.as_view(), name="single-consumer-trade" ),
path('<str:order_id>/producers/', SingleProducerTradeAPIView.as_view(), name="single-producer-trade" ),
Models.py:
from django.db import models
from authApi.models import User
class Order(models.Model):
user = models.ForeignKey(User,related_name='user',null=True, on_delete=models.CASCADE)
date = models.DateField(auto_now_add=True)
class Trade(models.Model):
consumer = models.ForeignKey(User,related_name='consumer',on_delete=models.CASCADE)
producer = models.ForeignKey(User,related_name='producer',on_delete=models.CASCADE)
order = models.ForeignKey(Order, related_name='trades',on_delete=models.CASCADE)
price = models.DecimalField(max_digits=10, max_length=255, decimal_places=2)
location = models.CharField(max_length=255)
energyQuantity = models.DecimalField(max_digits=10, max_length=255, decimal_places=2)
startTime = models.DateField(auto_now_add=True)
stopTime = models.DateField(auto_now_add=True)
Serializers.py:
class TradeSerializer(serializers.ModelSerializer):
class Meta:
model = Trade
fields = ('id',
'order_id',
'startTime',
'stopTime',
'price',
'consumer_id',
'producer_id',
'location',
'energyQuantity',
)
class OrderSerializer(serializers.ModelSerializer):
trades = TradeSerializer(read_only=True, many= True)
class Meta:
model = Order
fields = ('id',
'trades',
'date',
)
What i tried:
Views
class SingleProducerTradeAPIView(ListCreateAPIView):
serializer_class=TradeSerializer
queryset = Trade.objects.all()
permission_classes = (permissions.IsAuthenticated,)
lookup_fields = ('order_id', 'producer_id')
def list(self, *args, **kwargs):
order_id = kwargs.get('order_id')
user = self.request.user
try:
trades = Trade.objects.filter(order_id=order_id,consumer_id=user)
except Trade.DoesNotExist:
return JsonResponse({'message': 'The user does not exist'}, status=status.HTTP_404_NOT_FOUND)
order_serializer = TradeSerializer(trades, many=True)
return JsonResponse(order_serializer.data, safe=False)
def perform_create(self, serializer):
return serializer.save(consumer_id=self.request.user)
I want to be able to fetch from the list of trades(via the trade model and serializers) using the order_id and the producer_id.
Here you can directly use the get_queryset method and you will definitely get the parameters from kwargs of the get_queryset method and you can remove the queryset variable from the class and directly hit the query in the get_queryset method. It will save time by hitting the database only once.
there is no need for lookup_fields too so you can remove it.
class SingleConsumerTradeAPIView(ListCreateAPIView):
serializer_class=TradeSerializer
permission_classes = (permissions.IsAuthenticated,)
lookup_fields = ('order_id', 'producer_id')
def get_queryset(self, *args, **kwargs):
return Trade.objects.filter(order_id=kwargs.get('order_id'),producer_id=
self.request.user.id)
Django will send the parameters from the URL into the view functions are keyword arguments (**kwargs)
class SingleConsumerTradeAPIView(ListCreateAPIView):
serializer_class=TradeSerializer
queryset = Trade.objects.all()
permission_classes = (permissions.IsAuthenticated,)
lookup_fields = ('order_id', 'producer_id')
def list(self, *args, **kwargs):
order_id = kwargs.get('order_id')
user_id = kwargs.get('user_id')
# do some work with order_id and user_id
def get_queryset(self):
return self.queryset.filter(order_id=self.request.user,producer_id=
self.request.user)

Private messaging system Django

I've been trying to set up a basic private messaging system in Django using the generic CreateView.
I am currently having trouble with the "Receiver"/"To" field in my form. I tried to make it so it was a drop down field with the options being followers of the logged-in user.
Currently, the field is populating with the correct usernames (in this case, "testuser1") but it is throwing an error saying this field needs to be populated with an instance of the User object.
ValueError: Cannot assign "'testuser1'": "Message.reciever" must be a "User" instance.
Is there a way to have the form pass in the object of the username that is selected?
Model:
class Message(models.Model):
sender = models.ForeignKey(User, on_delete=models.CASCADE, related_name="sender")
reciever = models.ForeignKey(User, on_delete=models.CASCADE, related_name="reciever")
subject = models.CharField(max_length=128, default="-")
content = models.TextField()
send_date = models.DateTimeField(default=timezone.now)
User Relationships Model:
class UserRelationships(models.Model):
user_id = models.ForeignKey(User, on_delete=models.CASCADE, related_name="following")
following_user_id = models.ForeignKey(User, on_delete=models.CASCADE, related_name="followers")
created = models.DateTimeField(auto_now_add=True)
UPDATED Form:
class MessageCreateForm(forms.ModelForm):
class Meta:
model = Message
fields = ['sender', 'reciever', 'subject', 'content']
widgets = {'sender': forms.HiddenInput()}
def __init__(self, *args, **kwargs):
user = kwargs.pop('user')
follower_objects = kwargs.pop('follower_objects')
super(MessageCreateForm, self).__init__(*args, **kwargs)
self.fields['reciever'] = RecieverModelChoiceField(queryset=User.objects.filter(username__in=follower_objects))
View:
class MessageCreateView(LoginRequiredMixin, CreateView):
model = Message
template_name = 'message/compose.html'
form_class = MessageCreateForm
def get_initial(self):
initial = super().get_initial()
initial['sender'] = self.request.user
return initial
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
user = self.request.user
followers = user.followers.values_list('user_id', flat=True)
follower_objects = []
kwargs['user'] = self.request.user
kwargs['follower_objects'] = follower_objects
for id in followers:
follower = User.objects.get(id=id)
follower_objects.append(follower)
return kwargs
def form_valid(self, form):
form.instance.user = self.request.user
return super().form_valid(form)
You have to use forms.ModelChoiceField instead of forms.ChoiceField
ForeignKey (model) > ModelChoiceField (form) - Default widget: Select
ModelChoiceField has attribute queryset.
You can filter field reciever.queryset directly in MessageCreateForm.__init__ method.
def __init__(self, *args, **kwargs):
user = kwargs.pop('user')
super(MessageCreateForm, self).__init__(*args, **kwargs)
self.fields['reciever'].queryset = user.followers
UPDATE:
You can set a custom ModelChoiceField that will return any label you want (more info).
from django.forms import ModelChoiceField
class RecieverModelChoiceField(ModelChoiceField):
def label_from_instance(self, obj):
return obj.username
or
def __init__(self, *args, **kwargs):
....
self.fields['reciever'].label_from_instance = lambda obj: "%s" % obj.username

How to access a logged in user from a class based view?

How do I access a current logged in user from a class-based view?
In a function-based view we can pass a request parameter but I can't pass a request parameter from a class view.
I have seen ways to do it in the internet but I can't understand it.
my models.py file
class Category(models.Model):
name = models.CharField(max_length=255)
def __str__(self):
return (self.name)
def get_absolute_url(self):
return reverse("home")
class Post(models.Model):
title = models.CharField(max_length=255)
author = models.ForeignKey(User, on_delete=models.CASCADE)
body = models.TextField(max_length=3500)
category = models.CharField(max_length=255, default="uncategorized")
views.py
class HomeView(ListView, LoginRequiredMixin):
model = Post
template_name = "home.html"
Thank you.
You can use self.request.user inside methods of class-based views; as an example:
class HomeView(ListView, LoginRequiredMixin):
model = Post
template_name = "home.html"
def get_context_data(self):
current_loggedin_user = self.request.user
# ...
Edit (just print the username):
class HomeView(ListView, LoginRequiredMixin):
model = Post
template_name = "home.html"
def __init__(self, *args, **kwargs):
print(self.request.user, self.request.user.username) # print user & username
return super().__init__(*args, **kwargs)

Accessing ModelForm queryset Object field in template

In my ModelForm, I am filtering the project_users to a certain set of all Users. How can I customize the checkboxes to show a users first_name and last_name?
Currently showing their email address as the checkbox label.
models.py
class Project(models.Model):
project_business_profile = models.ForeignKey(BusinessProfile, on_delete=models.CASCADE)
project_users = models.ManyToManyField(User, related_name='project_users')
...
def __str__(self):
return str(self.project_name)
views.py
class EditProject(LoginRequiredMixin, View):
login_url = '/signin'
redirect_field_name = 'signin'
def get(self, request, project_id):
...
form = EditProjectForm(instance=project)
...
forms.py
class EditProjectForm(ModelForm):
project_users = forms.ModelMultipleChoiceField(
widget = forms.CheckboxSelectMultiple,
queryset = User.objects.none()
)
class Meta:
model = Project
fields = ['project_users']
def __init__(self, *args, **kwargs):
super(EditProjectForm, self).__init__(*args, **kwargs)
current_project = self.instance
current_business = current_project.project_business_profile
users = current_business.business_users.all()
self.fields['project_users'].queryset = current_business.business_users.all()
// Spits out the correct users however I need to access other user fields of User in template. Name etc
template
{{form.as_p}}
I'm not entirely sure I understand, do you just want to change the label which shows your user? If so something like this may work:
forms.py
class EditProjectForm(ModelForm):
project_users = forms.ModelMultipleChoiceField(
widget = forms.CheckboxSelectMultiple,
queryset = User.objects.none()
)
class Meta:
model = Project
fields = ['project_users']
def __init__(self, *args, **kwargs):
super(EditProjectForm, self).__init__(*args, **kwargs)
current_project = self.instance
current_business = current_project.project_business_profile
users = current_business.business_users.all()
self.fields['project_users'] = UserChoiceField(queryset=users)
// Spits out the correct users however I need to access other user fields of User in template. Name etc
class UserChoiceField(forms.ModelChoiceField):
def label_from_instance(self, obj):
user = User.objects.get(id=obj.id)
return user.get_full_name()

Categories