I wrote code to create new users via POST and DRF (Django Rest Framework) successfully and I can obtain token correctly, however when I try to POST (via DRF) to fill Profile linked to that user I get
(1048, "Column 'user_id' cannot be null")
This is snippets of my code:
for serializers:
class UserSerializer(ModelSerializer):
class Meta:
model = User
fields = ('username','email','first_name','last_name','password')
def create(self, *args, **kwargs):
user = super().create(*args, **kwargs)
p = user.password
user.set_password(p)
user.save()
return user
def update(self, *args, **kwargs):
user = super().update(*args, **kwargs)
p = user.password
user.set_password(p)
user.save()
return user
class ProfileSerializer(ModelSerializer):
class Meta:
model = Profile
fields = ('bio','birth_date','location','country')
def create(self, *args, **kwargs):
profile = super().create(*args, **kwargs)
profile.save()
return profile
def update(self, *args, **kwargs):
profile = super().update(*args, **kwargs)
profile.save()
return profile
and for views:
class ProfileViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows users to be viewed or edited.
"""
permission_classes = IsAuthenticated,
serializer_class = ProfileSerializer
queryset = Profile.objects.all()
class UserViewSet(viewsets.GenericViewSet,mixins.CreateModelMixin,):
"""
API endpoint that allows users to be viewed or edited.
"""
#permission_classes= (IsAdminUser,)
serializer_class = UserSerializer
queryset = User.objects.all().order_by('-date_joined')
and for models:
#receiver(post_save, sender = settings.AUTH_USER_MODEL)
def create_auth_token(sender,instance=None,created=False, **kwargs):
if created:
Token.objects.create(user=instance)
class Country(models.Model):
iso = models.CharField(max_length = 2,unique = True)
name = models.CharField(max_length = 250)
iso3 = models.CharField(max_length = 6)
phonecode = models.CharField(max_length=6)
def __unicode__(self):
return '%s' % self.name
def __str__(self):
return '%s' % self.name
class Profile(models.Model):
#user = models.OneToOneField(User, on_delete=models.CASCADE,default = "")
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE,default = "")
bio = models.TextField(max_length=100, blank=True)
birth_date = models.DateField(null=True, blank=True)
location = models.CharField(max_length=254, blank=True)
country = models.ForeignKey(Country,on_delete=models.CASCADE,blank=True,null=True)
def __unicode__(self):
return '%s' % self.user
def __str__(self):
return '%s' % self.user
#receiver(post_save, sender=User)
def update_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
instance.profile.save()
When I try with Postman and I pass a token for token authentication, it recognize the token correctly but I get this error:
(1048, "Column 'user_id' cannot be null") with Django DRF
I appreciate your help to solve this problem
Create() save to the database, so you need to set profile.user_id before calling the function.
Related
I am building a web app using REST API and I have a 4 users that I want them to access only the content they have added to the backend. I have created 3 users say farmers(3 farmers) and added content using each one of them. However, I have been unable to implement permissions such that a farmer can view and delete only what they added.
Here's is my code in models.py
User = get_user_model()
# Create your models here.
class Tender(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255)
author = models.ForeignKey(User, on_delete=models.CASCADE)
description = models.TextField()
date_due = models.DateField(default=datetime.date.today)
location = models.CharField(max_length=255, null=False)
contact = models.PositiveIntegerField(null=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.name
class Input(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255)
author = models.ForeignKey(User, on_delete=models.CASCADE)
description = models.TextField()
price = models.DecimalField(max_digits=10, decimal_places=2)
quantity = models.DecimalField(max_digits=10, decimal_places=2)
contact = models.PositiveIntegerField(null=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.name
class Investor(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255, unique=True)
author = models.ForeignKey(User, on_delete=models.CASCADE)
description = models.TextField()
location = models.CharField(max_length=255, null=False)
contact = models.PositiveIntegerField(null=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.name
Here's what I implemented in permissions.py
from rest_framework import permissions
class IsOwnerOrReadOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
return True
if request.user == obj.author:
return True
return False
class IsOwnerOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.user == obj.user :
return True
return False
Here is my code in views.py:
from requests import request
from rest_framework.viewsets import ModelViewSet
from rest_framework import viewsets, status, permissions
from django.http import Http404, JsonResponse
from rest_framework.views import Response
from .models import Tender, Input , Investor
from .serializers import TenderViewSerializer, InputViewSerializer, InvestViewSerializer
from user.models import User
from user.permissions import IsOwnerOnly, IsOwnerOrReadOnly
# Create your views here.
class TenderViewSet(viewsets.ModelViewSet):
serializer_class = TenderViewSerializer
permission_classes = (permissions.IsAuthenticated,)
def get_queryset(self):
if self.request.user.is_tender_holder:
queryset = Tender.objects.all()
return queryset
else:
return Response({"NO_ACCESS": "Access Denied"}, status=401)
def create(self, request, *args, **kwargs):
if self.request.user.is_tender_holder:
new_tender = Tender.objects.create(
name=request.data["name"],
description=request.data["description"],
date_due=request.data["date_due"],
location=request.data["location"],
contact=request.data['contact']
)
new_tender.save()
serializer = TenderViewSerializer(new_tender)
return Response(serializer.data)
else:
return Response({"NO_ACCESS": "Access Denied"}, status=401)
def destroy(self, request, *args, **kwargs):
if self.request.user.is_tender_holder:
try:
instance =self.get_object()
self.perform_destroy(instance)
except Http404:
pass
return Response({"message": "Tender deleted successfully"})
else:
return Response({"NO_ACCESS": "Access Denied"}, status=401)
# def update(self, request, *args, **kwargs):
class InvestorViewSet(ModelViewSet):
serializer_class = InvestViewSerializer
permission_classes = (permissions.IsAuthenticated,)
#queryset = Investor.objects.all()
def get_queryset(self):
if self.request.user.is_investor:
queryset = Investor.objects.all()
return queryset
else:
return Response({"NO_ACCESS": "Access Denied"}, status=401)
def create(self, request, *args, **kwargs):
if self.request.user.is_investor:
new_investment = Investor.objects.create(
name=request.data["name"],
description=request.data["description"],
location=request.data["location"],
contact=request.data['contact']
)
new_investment.save()
serializer = InvestViewSerializer(new_investment)
return Response(serializer.data)
else:
return Response({"NO_ACCESS": "Access Denied"}, status=401)
def destroy(self, request, *args, **kwargs):
if self.request.user.is_investor:
try:
instance =self.get_object()
self.perform_destroy(instance)
except Http404:
pass
return Response({"message": "Investment deleted successfully"})
else:
return Response({"NO_ACCESS": "Access Denied"}, status=401)
class InputViewSet(ModelViewSet):
serializer_class = InputViewSerializer
permission_classes = (permissions.IsAuthenticated, IsOwnerOrReadOnly)
#queryset = Input.objects.all()
def get_queryset(self, **kwargs):
if self.request.user.is_input_holder or self.request.user.is_superuser:
user = self.request.user
return Input.objects.filter(id=user.id)
else:
return Response({"NO_ACCESS": "Access Denied"}, status=401)
def create(self, request, *args, **kwargs):
if self.request.user.is_input_holder or self.request.user.is_superuser:
new_input = Input.objects.create(
name=request.data["name"],
description=request.data["description"],
price=request.data["price"],
quantity=request.data["quantity"],
contact=request.data['contact']
)
new_input.save()
serializer = InputViewSerializer(new_input)
return Response(serializer.data)
else:
return Response({"NO_ACCESS": "Access Denied"}, status=401)
def destroy(self, request, *args, **kwargs):
if self.request.user.is_input_holder or self.request.user.is_superuser:
try:
instance =self.get_object()
self.perform_destroy(instance)
except Http404:
pass
return Response({"message": "Input deleted successfully"})
else:
return Response({"NO_ACCESS": "Access Denied"}, status=401)
def has_object_permission(self, request, view, obj):
# Read permissions are allowed to any request,
# so we'll always allow GET, HEAD or OPTIONS requests.
if request.method in permissions.SAFE_METHODS:
return True
# Instance must have an attribute named `owner`.
return obj.owner == request.user
This code give view permission to all users
If you want to restrict all permission you should try something like this
def has_object_permission(self, request, view, obj):
# Instance must have an attribute named `owner`.
return obj.owner == request.user
Although it is in the title, I want to change the form dynamically with django.
But now I get an error.
I can't deal with it.
I was able to get user information, but if I filter it, it will be “cannot unpack non-iterable UPRM object”.
#forms.py
class RecordCreateForm(BaseModelForm):
class Meta:
model = URC
fields = ('UPRC','URN','UET','URT',)
def __init__(self, *args, **kwargs):
user = kwargs.pop('user')
super(RecordCreateForm,self).__init__(*args, **kwargs)
for field in self.fields.values():
field.widget.attrs['class'] = 'form-control'
self.fields['URN'].choices = UPRM.objects.filter(user=user)
#views.py
class RecordCreate(CreateView):
model = URC
form_class = RecordCreateForm
template_name = 'records/urcform.html'
success_url = reverse_lazy('person:home')
def get_form_kwargs(self):
kwargs = super(RecordCreate, self).get_form_kwargs()
# get users, note: you can access request using: self.request
kwargs['user'] = self.request.user
return kwargs
#models
class UPRM(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
URN = models.CharField( max_length=30,editable=True)
def __str__(self):
return self.URN
class URC(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
UPRC = models.CharField(max_length=300)
URN = models.ForeignKey(UPRM, on_delete=models.CASCADE)
def __str__(self):
return self.UPRC
cannot unpack non-iterable UPRM object
You should use queryset instead of choices here:
class RecordCreateForm(BaseModelForm):
class Meta:
model = URC
fields = ('UPRC','URN','UET','URT',)
def __init__(self, *args, **kwargs):
user = kwargs.pop('user')
super(RecordCreateForm,self).__init__(*args, **kwargs)
for field in self.fields.values():
field.widget.attrs['class'] = 'form-control'
self.fields['URN'].queryset = UPRM.objects.filter(user=user)
I created an update screen for django. However, although it redirects to successURL, the data has not been updated. I don't know why.
I need your help.
I will post it if necessary.
#view
class RecordDetailEdit(UpdateView,LoginRequiredMixin):
template_name = 'records/detail_edit.html'
model = URC
form_class = RecordDetailEditForm
pk_url_kwarg = 'id'
success_url = reverse_lazy('person:home')
def get_object(self):
return get_object_or_404(User, pk=self.request.user.user_id)
def get_form_kwargs(self):
kwargs = super(RecordDetailEdit, self).get_form_kwargs()
# get users, note: you can access request using: self.request
kwargs['user'] = self.request.user
return kwargs
#form
class RecordDetailEditForm(forms.ModelForm):
class Meta:
model = URC
fields = ('UPRC','URN','UET','URT')
def __init__(self, *args, **kwargs):
user = kwargs.pop('user')
super(RecordDetailEditForm, self).__init__(*args, **kwargs)
self.fields['URN'].queryset = UPRM.objects.filter(user=user)
#model
class URC(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
UPRC = models.CharField(max_length=300)
URN = models.ForeignKey(UPRM, on_delete=models.CASCADE)
UET = models.DurationField(editable=True)
URT = models.DateTimeField(default=timezone.now,editable=True)
group = models.ForeignKey(group, on_delete=models.CASCADE, null=True)
def __str__(self):
return self.UPRC
#url
path('<id>/edit/', views.RecordDetailEdit.as_view(), name='record_detail_edit'),
I changed it to the following.
def get_object(self, queryset=None):
obj = URC.objects.get(id=self.kwargs['id'])
return obj
i have created soft delete in my project and it is working fine. But the problem is that i am using User in-built model and OneToOneField with UserProfile. Now soft_delete function is in UserProfile Model where im using generic.DeleteView to delete user. The problem is that i could not pass object of User to UserProfile to set user_is_deleted to True. Here is my Code.
Views.py
class UserDeleteView(LoginRequiredMixin, generic.DeleteView):
model = User
template_name = 'users/user_confirm_delete.html'
success_url = '/users/'
def delete(self, request, *args, **kwargs):
self.object = self.get_object()
self.object.soft_delete()
return HttpResponseRedirect(self.get_success_url())
Models.py
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
user_company = models.ForeignKey(Company, on_delete=models.CASCADE)
user_role = models.ForeignKey(Roles, on_delete=models.CASCADE)
user_is_deleted = models.BooleanField(default=False)
user_deleted_at = models.DateTimeField(blank=True, null=True)
def soft_delete(self):
self.user_is_deleted = True
self.user_deleted_at = timezone.now() - tdelta(days=-1)
self.save()
Soft Delete Reference - https://blog.khophi.co/soft-delete-django-quickly/
Change this
#classmethod
def soft_delete(self):
...
Change your delete() method as,
def delete(self, request, *args, **kwargs):
self.object = self.get_object()
self.object.userprofile.soft_delete()
return HttpResponseRedirect(self.get_success_url())
i get this error when i am trying to update modified_by field
Tried to update field sales.CustomersTag.modified_by with a model instance, <SimpleLazyObject: <UserProfile: Admin>>. Use a value compatible with CharField.
this is my serializer.py:
class CustomersTagSerializer(serializers.ModelSerializer):
created_by = serializers.CharField(read_only=True, default=serializers.CurrentUserDefault())
modified_by = serializers.CharField(read_only=True, default=serializers.CurrentUserDefault())
def update(self, instance, validated_data):
instance.name = validated_data.get('name', instance.name)
instance.modified_by = validated_data.get('modified_by', instance.modified_by)
instance.save()
return instance
class Meta:
model = models.CustomersTag
fields = (
'id',
'name',
'created_date',
'modified_date',
'created_by',
'modified_by',
)
and this my view.py:
class CustomerTagGetIdPutView(generics.RetrieveAPIView,
mixins.UpdateModelMixin):
permission_classes = (AllowAny,)
queryset = models.CustomersTag.objects.all()
serializer_class = CustomersTagSerializer
def get_object(self):
id = self.kwargs['id']
obj = generics.get_object_or_404(models.CustomersTag, id=id)
return obj
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
def patch(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
i tried alot to solve this problem but i can't .. any one can help me for this problem
If you're using Django REST Framework generic views and no overriding the behavior of methods like get_serializer or get_serializer_context, your serializer will receive a context object. This context object is a dictionary with the request and the view object.
That said, you can do this by overriding create() and update() in your serializer. For example:
class CustomersTagSerializer(serializers.ModelSerializer):
class Meta:
model = models.CustomersTag
fields = (
'id',
'name',
'created_date',
'modified_date',
'created_by',
'modified_by',
)
def create(self, validated_data):
user = self.context['request'].user
return models.CustomersTag.objects.create(
created_by=user, **validated_data)
def update(self, instance, validated_data):
user = self.context['request'].user
instance.name = validated_data.get('name', instance.name)
instance.modified_by = user
instance.save()
return instance
But maybe if you want to maintain a log history of editions in your models you could use a package like django-auditlog.
You can do this while calling save() in your model.
For example:
class CustomersTagSerializer(serializers.ModelSerializer):
created_by = models.ForeignKey(User, null=True, editable=False)
modified_by = models.ForeignKey(User, null=True, editable=False)
def save(self, *args, **kwargs):
user = get_current_user()
if user and user.is_authenticated():
self.modified_by = user
if not self.id:
self.created_by = user
super(CustomersTagSerializer, self).save(*args, **kwargs)