Want to send extra data from BasePermission to Queryset in ModelVIewSet DRF - django

Here is My Seller Model which has User as OneToOneField
#models.py
.
.
class CompanyProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="user")
company = models.CharField(max_length=255)
pincode = models.IntegerField()
city = models.ForeignKey(City, on_delete=models.CASCADE)
address = models.TextField()
profileActive = models.BooleanField(default=False)
.
.
Purchase Model
#models.py
class PurchaseOrder(models.Model):
company = models.ForeignKey(CompanyProfile, on_delete=models.CASCADE)
message = models.TextField(blank=True, null=True)
expectedDelivery = models.DateField(blank=True, null=True)
isCancel = models.BooleanField(default=False))
updated_at = models.DateTimeField(auto_now=True)
#Custom permission_classes
class SellerCompanyActive(BasePermission):
message = 'Only Seller with activated company account is allowed'
def has_permission(self, request, view):
user = AuthUser.objects.get(id=request.user.id)
if user.is_seller:
try:
company = CompanyProfile.objects.get(user=user)
if company.profileActive:
return True
else:
self.message = "Company profile is not active"
except CompanyProfile.DoesNotExist:
self.message = "Company not found first create company"
return False
In ModelViewSet
#views.py
class SellerPurchaseOrder(viewsets.ModelViewSet):
queryset = PurchaseOrder.objects.all()
serializer_class = PurchaseOrderSerializer
authorization_classes = [TokenAuthentication]
permission_classes = [IsAuthenticated, SellerCompanyActive]
def get_queryset(self):
user = self.request.user
company = CompanyProfile.objects.get(user=user)
return self.queryset.filter(company=company)
Now here I always had to use this
user = self.request.user
company = CompanyProfile.objects.get(user=user)
As their are lot of other views also, Is their any other way to send data from my custom permission_classes i.e from SellerCompanyActive to direct SellerPurchaseOrder->get_queryset

You can set message attribute to be a dict, something like this:
class SellerCompanyActive(BasePermission):
message = {'error': 'Only Seller with activated company account is allowed'}
def has_permission(self, request, view):
...
you can add more fields to the dictionary to send more data

Related

Django Multiple table to authentication

I'd like to have 3 differents table and use them to authenticate.
The Contact will authenticate himself with his phone number, ChainStore with his identifier and User (django user admin) will have his normal behavior (with username). ChainStore and Contact have forms to authenticate, how can i do ?
Here is my models:
class Contact(models.Model):
id = models.UUIDField(primary_key=True, default=uuid4)
first_name = models.CharField(max_length=10)
last_name = models.CharField(max_length=10)
address = models.ForeignKey(
Address, on_delete=models.CASCADE, blank=True, null=True
)
email = models.CharField(blank=True, null=True, max_length=140)
mobile = models.CharField(max_length=12, unique=True)
password = models.CharField(max_length=128)
optin_email = models.BooleanField(default=False)
optin_sms = models.BooleanField(default=False)
class ChainStore(models.Model):
id = models.UUIDField(primary_key=True, default=uuid4)
password = models.CharField(max_length=64)
last_login = models.DateTimeField(blank=True, null=True)
rules = models.JSONField(default=dict)
I've tried to use inheritance with AbstractBaseUser but i had problems like
auth.User.groups: (fields.E304) Reverse accessor for 'User.groups' clashes with reverse accessor for 'Contact.groups'
My Forms:
class ContactSignUp(forms.Form):
first_name = forms.CharField(max_length=10)
last_name = forms.CharField(max_length=10)
email = forms.EmailField()
mobile = forms.RegexField(max_length=12)
password = forms.CharField(max_length=32)
password_verification = forms.CharField(max_length=32)
class ContactAuth(forms.Form):
mobile = forms.CharField(max_length=12)
password = forms.CharField(max_length=32)
My views:
class ContactSignUpView(FormView):
form_class = ContactSignUp
model = Contact
template_name = "sign_up.html"
success_url = "/"
def form_valid(self, form):
"""This method is called when valid form data has been POSTed."""
Contact(
first_name=form.cleaned_data["first_name"],
last_name=form.cleaned_data["last_name"],
email=form.cleaned_data["email"],
mobile=form.cleaned_data["mobile"],
password=form.cleaned_data["password"]
).save()
return super().form_valid(form)
class ContactAuthView(FormView):
form_class = forms.ContactAuth
model = Contact
template_name = "auth.html"
success_url = "/"
def form_valid(self, form):
"""This method is called when valid form data has been POSTed."""
user = ContactBackend().authenticate(
form.cleaned_data["mobile"],
form.cleaned_data["password"],
)
return super().form_valid(form)
My backend:
class ContactBackend(BaseBackend):
"""
Mobile Authentication Backend
Allows a user to sign in using an mobile/password pair rather than
a username/password pair.
"""
def authenticate(self, username=None, password=None):
try:
contact = models.Contact.objects.get(mobile=username)
if check_password(password, contact.password): # Imported from django.contrib.auth.hashers
return contact
except models.Contact.DoesNotExist:
return None
def get_user(self, user_id):
try:
return models.Contact.objects.get(pk=user_id)
except models.Contact.DoesNotExist:
return None
And finally my settings:
AUTH_USER_MODEL = "auth.User"
AUTHENTICATION_BACKENDS = [
"django.contrib.auth.backends.ModelBackend",
"contact.backends.ContactBackend"
]

Fetch field Details for a login use in Django REST Framework with foreignkeyfield Relationship

I am trying to add Value into InstantInvestment Model in Django REST Framework which is working. but, only want to show the shipping that is specifically for the login user in. which means, the present situation is giving all the shipping not for this user.
models.py
class Shipping(models.Model):
investor = models.ForeignKey(User, related_name='shipping', on_delete=models.CASCADE)
beneficiary = models.CharField("Beneficiary Name", max_length=150)
bank = models.ForeignKey(Bank, related_name="bank", on_delete=models.CASCADE)
account = models.CharField(max_length=10)
address = models.TextField("Shipping Adresss")
created_at = models.DateTimeField(auto_now_add=True)
update_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.beneficiary
class Meta:
verbose_name = 'Shipping'
verbose_name_plural = 'Shippings'
class InstantInvestment(models.Model):
investor = models.ForeignKey(User, related_name='instantivestment', on_delete=models.CASCADE)
ref_code = models.CharField(max_length=200, blank=True, null=True)
investment = models.FloatField("Investment in dollar")
rate = models.FloatField("Exchange Rate")
transferable = models.FloatField("Money Transferable")
conversion = models.FloatField("Rate in Naira")
product = models.ForeignKey(Product, related_name='instant_product', on_delete=models.CASCADE)
shipping = models.ForeignKey(Shipping, related_name='shipping', on_delete=models.CASCADE)
done = models.BooleanField("Completed Transaction", default=False)
created_at = models.DateTimeField(auto_now_add=True)
update_at = models.DateTimeField(auto_now=True)
def __str__(self):
return f'{self.investor.get_full_name()} - Transaction Code: {self.ref_code}'
class Meta:
verbose_name = 'InstantInvestment'
verbose_name_plural = 'InstantInvestments'
serializers.py
class ShippingSerializer(serializers.ModelSerializer):
class Meta:
model = Shipping
fields = ('beneficiary', 'bank', 'account', 'address')
class QucikPaymentSerializer(serializers.ModelSerializer):
class Meta:
model = InstantInvestment
fields = ('url', 'id','investment', 'rate', 'transferable', 'conversion', 'product', 'shipping')
views.py
class QuickPaymentView(viewsets.ModelViewSet):
queryset = InstantInvestment.objects.all()
serializer_class = QucikPaymentSerializer
permission_classes = [ permissions.IsAuthenticated ]
def get_queryset(self):
return InstantInvestment.objects.filter(investor=self.request.user, done=False)
def perform_create(self, serializer):
serializer.save(investor=self.request.user)
Remove the query set attribute in your viewset class
class QuickPaymentView(viewsets.ModelViewSet):
serializer_class = QucikPaymentSerializer
permission_classes = [ permissions.IsAuthenticated ]
def get_queryset(self):
return InstantInvestment.objects.filter(investor=self.request.user, done=False)
def perform_create(self, serializer):
serializer.save(investor=self.request.user)
to make it work you need to specify the basename key word argument when you register your viewset class with router.
router.register(r'quickpayment/' , QuickPaymentView , basename='InstantInvestment')
If you want do so you need to write a html separately. I think that you showed in your question is rest frameworks ui to test the api. That UI can't determine the User before you send the request.

Unique ManyToManyField DRF

I do not want to be able to create multiple Chat objects with the same EXACT participants field.
For example:
If a chat already exists, with participants=["user1", "user2"],
I do not want to be able to create a new chat objects with the same EXACT participants
Looking for something like unique=True, except for manytomanyfield.
Models:
class Contact(models.Model):
user = models.ForeignKey(
User, related_name='friends', on_delete=models.CASCADE)
friends = models.ManyToManyField('self', blank=True)
def __str__(self):
return self.user.username
class Message(models.Model):
contact = models.ForeignKey(
Contact, related_name="messages", on_delete=models.CASCADE)
content = models.TextField()
timestamp = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.contact.user.username
class Chat(models.Model):
participants = models.ManyToManyField(
Contact, related_name='chats')
messages = models.ManyToManyField(Message, blank=True)
def __str__(self):
return f"{self.pk}"
Serializer:
class ContactSerializer(serializers.StringRelatedField):
def to_internal_value(self, value):
return value
class ChatSerializer(serializers.ModelSerializer):
participants = ContactSerializer(many=True)
class Meta:
model = Chat
fields = ("id", "messages", "participants")
read_only = ('id')
def create(self, validated_data):
print(validated_data)
participants = validated_data.pop('participants')
# for c in Chat.participant_set.all():
# print(c)
chat = Chat()
chat.save()
for username in participants:
contact = get_user_contact(username)
chat.participants.add(contact)
chat.save()
return chat
Probably you can try like this:
participants = validated_data.pop('participants')
prev_chat = Chat.objects.all()
for username in participants:
prev_chat = prev_chat.filter(participants__username=username)
if prev_chat.exists():
chat = prev_chat.first()
else:
chat = Chat()
chat.save()
for username in participants:
contact = get_user_contact(username)
chat.participants.add(contact)

how to get Json response formatted differently on django

I have a custom user model and i implemented a user following and follower system that works well. But the problem I have is that when I request for the followers a particular user has, the JSON response I get is not exactly the way I want it returned. more details below
models
class User(AbstractBaseUser, PermissionsMixin):
username = None
email = models.EmailField(max_length=254, unique=True)
name = models.CharField(max_length=250)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
last_login = models.DateTimeField(null=True, blank=True)
date_joined = models.DateTimeField(auto_now_add=True)
slug = models.SlugField(max_length=255, unique=True, blank=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['name']
class FollowLog(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='followers')
followed_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE,
related_name='following', null=True)
followed_on = models.DateTimeField(auto_now_add=True)
status = models.CharField(choices=FOLLOW_STATUS, default=FollowStatus.following.value, max_length=30)
updated_on = models.DateTimeField(auto_now=True)
unfollowed_on = models.DateTimeField(null=True)
blocked_on = models.DateTimeField(null=True)
serializer
class FollowerSerializer(serializers.ModelSerializer):
'''
Allows people to view follower of a user
'''
followed_by = serializers.SlugRelatedField(read_only=True, slug_field='slug')
class Meta:
model = FollowLog
fields = ('followed_by',)
read_only_fields = ('followed_by',)
view
class UserFollowerView(APIView):
'''
Gets all the followers to a user
'''
permission_classes = [AllowAny]
def get(self, request, slug):
user = User.objects.get(slug=slug)
followers = user.followers.all().filter(status='following').order_by("-followed_on")
serializer = FollowerSerializer(followers, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
when I run the above, I get the appropriate response but in this format
JSON response I get
[
{
"followed_by": "opeyemi-odedeyi-qqmunu13o4b8vd4"
},
{
"followed_by": "modupeoluwa-odedeyi-m7ji5qj5szu2uqo"
}
]
I would prefer it if I got a response like below
preferred response I want to get
{
"followed_by": [
"opeyemi-odedeyi-qqmunu13o4b8vd4",
"modupeoluwa-odedeyi-m7ji5qj5szu2uqo"
]
}
A serializer will always return a dictionary, and if you set many=true, it will return an array of dictionaries.
What you can do is to get the array you want from the array of dictionaries returned by the serializer:
class UserFollowerView(APIView):
'''
Gets all the followers to a user
'''
permission_classes = [AllowAny]
def get(self, request, slug):
user = User.objects.get(slug=slug)
followers = user.followers.all().filter(status='following').order_by("-followed_on")
data = FollowerSerializer(followers, many=True).data
data_to_return = list(map(lambda item: item['followed_by'], data))
return Response(data_to_return, status=status.HTTP_200_OK)
You can make a custom renderer for your api and change the format of the response however so you want.
class UserFollowerView(APIView):
'''
Gets all the followers to a user
'''
permission_classes = [AllowAny]
renderer_classes = (UserFollowerRenderer, )
def get(self, request, slug):
user = User.objects.get(slug=slug)
followers = user.followers.all().filter(status='following').order_by("-followed_on")
serializer = FollowerSerializer(followers, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
and the renderer can be something like this :-
class UserFollowerRenderer(CoreJSONRenderer):
charset = "utf-8"
def render(self, data, media_type=None, renderer_context=None):
if not data:
return "[]"
final_data = list()
for element in data:
final_data.append(element.get('followed_by'))
result = dict(followed_by=final_data)
return json_dumps(result)

Nested API View of Django REST Framework?

At the moment I developed the following code, for me to get the Contact List of each user. The views return the ID numbers of the Contacts of the User. I need to get, instead of the ID numbers, the 'name' and 'last_name' attribute of said contacts. I am quite new to Django's REST Framework and I'm not quite sure what to do next but I believe I have to nest the APIView. I would really appreciate some help!
views.py
def list_contacts(request, id_profile):
url = request.build_absolute_uri(reverse('api_users:contact_list', kwargs={'pk':id_profile}))
response = requests.get(url)
profile = Profile.objects.get(pk=id_profile)
if response.status_code == status.HTTP_200_OK:
data = response.content
user = json.loads(data)
return render(request, 'profiles/contact_list.html', {'user':user})
models.py
class Profile(models.Model):
id_user = models.OneToOneField(User, on_delete=models.CASCADE)
name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
birthday = models.DateField(auto_now=False)
description = models.CharField(max_length=100)
profile_picture = models.ImageField(upload_to='images/profiles/%Y/%m/%d', blank=False)
active = models.BooleanField(default = False)
contacts = models.ManyToManyField('self', blank=True, default='null')
created_at = models.DateTimeField(default=timezone.now)
updated_at = models.DateTimeField(default=timezone.now)
deleted_at = models.DateTimeField(blank=True, null=True)
class Meta:
ordering = ('-id',)
def __str__(self):
return self.name+' '+self.last_name
def active_profiles():
return Profile.objects.filter(active=True)
api/views.py
class ContactListView(generics.ListAPIView):
queryset = Profile.objects.all()
serializer_class = UserContactListSerializer
filter_backends = (filters.SearchFilter,)
search_fields = ('name', 'last_name',)
def get(self, request, pk, format=None):
contacts = Profile.objects.get(pk=pk)
serializer = UserContactListSerializer(contacts)
return Response(serializer.data)
api/serializers.py
class UserContactListSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = ['name','last_name','contacts']
I don't know what exactly is going on in your list_contacts but if you want to use the same serializer as a field in itself, you currently can't.
While Django models allow you to use 'self' as the reference, DRF doesn't.
What you can instead do is create another serializer and use that as the field.
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = ("id", "first_name", "last_name")
class UserContactListSerializer(serializers.ModelSerializer):
contacts = UserSerializer(many=True)
class Meta:
model = Profile
fields = ("id", "first_name", "last_name", "contacts")