How to update self.request.user field in django rest framework? - python

I need to update the requested user field when I create the organization from OrganizationViewSet as below,
class OrganizationViewSet(viewsets.ModelViewSet):
queryset = Organization.objects.all()
serializer_class = OrganizationSerializer
permission_classes = [permissions.IsAuthenticated]
def perform_create(self, serializer):
serializer.save(admin_user=self.request.user)
data = serializer.data
org_id = data['id']
self.request.user.update(organization=org_id) # Error is coming from this line
The above code generates the following error,
'User' object has no attribute 'update'
Here is my User models.py file
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(_('email address'), unique=True)
organization = models.ForeignKey(
"organization.Organization", on_delete=models.CASCADE, null=True, blank=True)
first_name = models.CharField(max_length=200, blank=True)
last_name = models.CharField(max_length=200, blank=True)
phone = models.CharField(max_length=20, blank=True)
So my question is, how can I update the requested user organization? Any help?

update is a method on the QuerySet and not on a Model
You can do model.save as follows to have the desired behavior
self.request.user.organization_id = org_id
self.request.user.save()

Related

(DRF) How to update a foreignkey field

I have two models, Account model & Thread model:
class Account(AbstractBaseUser, PermissionsMixin):
class Meta:
verbose_name_plural = "Account List"
email = models.EmailField(max_length=255, unique=True)
username = models.CharField(max_length=255, unique=True)
name = models.CharField(max_length=255, default="")
profile_image = models.ImageField(max_length=255, upload_to=profile_image_path, blank=True, null=True, unique=True)
about = models.TextField(max_length=255, default='Write something about yourself...', blank=True)
start_date = models.DateTimeField(default=timezone.now)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
last_login = models.DateTimeField(auto_now=True)
objects = AccountManager()
USERNAME_FIELD = "email"
REQUIRED_FIELDS = ["username", "name"]
def __str__(self):
return self.username
class Thread(models.Model):
options = (('active', 'Active'), ('deactivated', 'Deactivated'))
username = models.ForeignKey(Account, on_delete=models.CASCADE, to_field='username')
alt = models.TextField(max_length=255, blank=True)
image = models.ImageField(max_length=255, upload_to=thread_image_path, blank=True)
content = models.TextField(blank=True)
created = models.DateTimeField(blank=True, null=True, default=timezone.now)
status = models.CharField(max_length=11, choices=options, default='active')
If I have already created a thread that is ForeignKey to the Account model, I am not able to change the username of the Account model, returning the error FOREIGN KEY constraint failed. I guess the existing Thread model require a username to point to. Is there way to create a custom update method in view.py to update the ForeignKey automatically?
Here is my view.py:
class UserViewSet(viewsets.ModelViewSet):
serializer_class = UserSerializer
queryset = Account.objects.all()
permission_classes = (AllowAny,)
EDIT:
serializer.py
class ThreadSerializer(serializers.ModelSerializer):
profile_image = serializers.SerializerMethodField('get_profile_image')
created = serializers.DateTimeField(format="%d %B, %Y %H:%M:%S")
class Meta:
model = Thread
fields = (
'id',
'username',
'profile_image',
'alt',
'image',
'content',
'created',
'status')
def get_profile_image(self, thread):
profile_image_url = thread.username.profile_image.url
return profile_image_url
Error:
IntegrityError at /account/auth/user/1/
FOREIGN KEY constraint failed
Request Method: PUT
Request URL: http://127.0.0.1:8000/account/auth/user/1/
Django Version: 4.0.4
Exception Type: IntegrityError
Exception Value:
FOREIGN KEY constraint failed
Exception Location: c:\Users\85291\Desktop\vscode\my-app\web\env\lib\site-packages\django\db\backends\sqlite3\base.py, line 477, in execute
Python Executable: c:\Users\85291\Desktop\vscode\my-app\web\env\Scripts\python.exe
Python Version: 3.10.2
Python Path:
['C:\\Users\\85291\\Desktop\\vscode\\my-app\\web\\jtravel',
'c:\\Users\\85291\\.vscode\\extensions\\ms-python.python-2022.6.3\\pythonFiles\\lib\\python\\debugpy\\_vendored\\pydevd',
'C:\\Python310\\python310.zip',
'C:\\Python310\\DLLs',
'C:\\Python310\\lib',
'C:\\Python310',
'c:\\Users\\85291\\Desktop\\vscode\\my-app\\web\\env',
'c:\\Users\\85291\\Desktop\\vscode\\my-app\\web\\env\\lib\\site-packages']
Server time: Sun, 05 Jun 2022 16:40:53 +0800
delete to_field='username'
username = models.ForeignKey(Account, on_delete=models.CASCADE)
it is a reason of an error

Django: How do I check if a User has already read/seen an article?

I am building a web-application for my senior design project with Python and Django. I have a user model that is able to read/write articles to display on the website. I have some tasks I want to accomplish.
I want to make it so that if an article is accessed (read) by a user, it is indicated for only that user that the article has been previously accessed. If I were to log into a brand new user account, the same article wouldn't be indicated as "accessed" for the new account.
How would I be able to present on the front-end side that the article has been viewed by the user logged in? (ie: make the article title bolded or a different color to indicate its been already visited)
Below are my models and views:
User model
class User(AbstractBaseUser, PermissionsMixin):
first_name = models.CharField(max_length=30, blank=True)
last_name = models.CharField(max_length=30, blank=True)
email = models.EmailField(unique=True, max_length=255, blank=False)
university = models.CharField(max_length=200, null=True, blank=True)
newsletter_subscriber = models.BooleanField(default=False)
is_email_verified = models.BooleanField(default=False)
is_staff = models.BooleanField(
default=False,
help_text=(
'Designates whether the user can log into '
'this admin site.'
),
)
is_active = models.BooleanField(
default=True,
help_text=(
'Designates whether this user should be '
'treated as active. Unselect this instead '
'of deleting accounts.'
),
)
date_joined = models.DateTimeField(default=timezone.now)
objects = UserManager()
USERNAME_FIELD = 'email'
def __str__(self):
return self.email
Article model
class Article(models.Model):
user = models.ForeignKey(
User, on_delete=models.DO_NOTHING, null=True, blank=True)
title = models.CharField(max_length=200, null=True, blank=False)
author = models.CharField(max_length=200, null=True, blank=False)
year = models.CharField(max_length=200, null=True, blank=False)
journal = models.CharField(max_length=200, null=True, blank=False)
description = models.TextField(null=True, blank=True)
URL = models.CharField(max_length=200, null=True, blank=False)
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
class MetaData:
ordering = ['-created']
Article detail view
class ArticleDetail(LoginRequiredMixin, DetailView):
model = Article
context_object_name = 'articles'
template_name = 'home/article_detail.html'
Thank you!
You could create an extra table.
class ArticleSeenRecord(models.Model):
user = models.ForeignKey("django.contrib.auth.models.User", on_delete=models.CASCADE)
article = models.ForeignKey(Article, on_delete=models.CASCADE)
And then in your article view, create a new record when one doesn't exist, for that article combined with the authenticated user.
class ArticleDetail(LoginRequiredMixin, DetailView):
model = Article
context_object_name = 'articles'
template_name = 'home/article_detail.html'
def get_object(self, queryset=None):
obj = super().get_object(queryset)
record, created = ArticleSeenRecord.objects.get_or_create(user=self.request.user, article=obj)
return obj
class Article(models.Model):
...
def seen_by_user(self, user):
return self.atricleseenrecord_set.objects.filter(user=user).exists()
I added the extra function here. You will also need to add a template tag which you can ideally copy from this example
#register.simple_tag
def article_seen_by_user(article, user):
return article.seen_by_user(user)
For further guidance on how to use and register custom template tags, please refer to this page of the documentation:
https://docs.djangoproject.com/en/4.0/howto/custom-template-tags/
Specifically this section:
https://docs.djangoproject.com/en/4.0/howto/custom-template-tags/#django.template.Library.simple_tag

save() prohibited to prevent data loss due to unsaved related object 'user' with Foreign Keys

I am trying to POST a new User in User table and assign attribute to this user in the next related table UserAttribute. UserAttribute table contains 'user' field, which is foreign key to the User table.
User.id = UserAttribute.user
My POST method creates a new record in User table, hovewer I can not create relation in UserAttribute table. I am getting:
save() prohibited to prevent data loss due to unsaved related object 'user'.
It is strange because the User is successfully created in User table.
I tried to save record in two ways. You can find the code on belove.
What is more, I tried transactions too.
models.py
class User(models.Model):
id = models.IntegerField(primary_key = True)
email = models.CharField(unique=True, max_length=180)
roles = models.JSONField(default=dict)
password = models.CharField(max_length=255, blank=True, null=True)
name = models.CharField(max_length=255, blank=True, null=True)
firebase_id = models.CharField(max_length=255, blank=True, null=True)
created_at = models.DateTimeField(default=now)
progress_sub_step = models.IntegerField(blank=True, null=True)
step_available_date = models.DateTimeField(blank=True, null=True)
progress_step = models.IntegerField(blank=True, null=True)
active = models.IntegerField(default=1)
last_login_at = models.DateTimeField(blank=True, null=True)
class Meta:
managed = False
db_table = 'user'
class Attribute(models.Model):
name = models.CharField(max_length=255)
value = models.CharField(max_length=255)
class Meta:
managed = False
db_table = 'attribute'
class UserAttribute(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name = 'attribute')
attribute = models.ForeignKey(Attribute, on_delete=models.CASCADE)
class Meta:
managed = False
db_table = 'user_attribute'
serializers.py
class UserAttributeSerializer(serializers.ModelSerializer):
class Meta:
model = UserAttribute
fields = ('attribute',)
class UserSerializer(serializers.ModelSerializer):
attribute = UserAttributeSerializer(many = True)
class Meta:
model = User
fields = ('email', 'name', 'firebase_id', 'attribute')
#transaction.atomic()
def create(self, validated_data):
attributes = validated_data["attribute"]
del validated_data["attribute"]
firebase_id_v = validated_data.get('firebase_id')
user_save = User.objects.create(**validated_data)
# Second method
# user_save = User(**validated_data)
# user_save.save()
for attribute in attributes:
a = UserAttribute.objects.create(user = user_save, **attribute)
return user
views.py
class UserAttViewSet(viewsets.ModelViewSet):
queryset = UserAttribute.objects.all()
serializer_class = UserAttributeSerializer
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer

django_filters search on field in ManyToMany through model

I have a manytomany relationship with a through model (joint table). I'd like to utilize the search_fields in DRF or other custom Filter to filter on a field in the through model.
model 1:
class Company(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255, blank=False, null=False)
source = models.CharField(max_length=255, blank=False, null=False)
ein_number = models.CharField(max_length=255, blank=True, null=False)
record_keepers = models.ManyToManyField(
'record_keepers.RecordKeepers', through='record_keepers.CompanyRecordKeepers',
related_name='record_keepers')
model 2 (through model):
class CompanyRecordKeepers(models.Model):
id = models.AutoField(primary_key=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
filing_year = models.IntegerField(blank=False, null=False)
company = models.ForeignKey('employers.Company', on_delete=models.PROTECT,
blank=True, null=False)
record_keeper = models.ForeignKey('RecordKeepers', on_delete=models.PROTECT, blank=True, null=False)
model 3:
class RecordKeepers(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255, unique=True, blank=True, null=True)
Now in my view, I'd like to search for companies who filed in the filing_year 2019 and whose record_keeper name is contains some_value
View:
class ListCompanyView(generics.ListAPIView):
serializer_class = CompanySerializer
permission_classes = [IsAdminUser]
filter_backends = (SearchFilter,)
search_fields = ['companyrecordkeepers__filing_year']
queryset = Company.objects.all()
Ideally I could do a GET request with some params to filter on:
?companyrecordkeepers__filing_year=2019&name=some_value
Any idea how to accomplish this?
If you don't specify a related_name you need to add the suffix _set whenever querying a reverse relation. Also, a SearchFilter is for simple single query parameter so for your case it's better to define them as filterset_fields. https://www.django-rest-framework.org/api-guide/filtering/#filtering-against-query-parameters
class ListCompanyView(generics.ListAPIView):
serializer_class = CompanySerializer
permission_classes = [IsAdminUser]
filter_backends = [DjangoFilterBackend]
filterset_fields = ['companyrecordkeepers_set__filling_year', 'name']
queryset = Company.objects.all()

How to filter a User model with a ForeignKey using custom URL format

Intended URL format:
api/v1/users?applicant={applicantId}
Current (working) URL format:
path('users/applicant/<int:pk>/')
How do I check if an applicant has already setup an account on the system as User using the applicantID (which has a foreignkey relationship with the Users table)?
models.py:
class Applicant(models.Model):
APPLICATION_STATUS = (
(1, 'Pending'),
(2, 'Accept'),
(3, 'Reject'),
)
first_name = models.CharField(max_length=200, blank=False,
null=False)
last_name = models.CharField(max_length=200, blank=False,
null=False)
email = models.EmailField(max_length=200, blank=False, null=False,
unique=True)
phone_regex = RegexValidator(regex=r'^\+?1?\d{9,15}$',
message="Phone number must be entered in the format: '+999999999'.
Up to 15 digits allowed.")
phone_number = models.CharField(validators=[phone_regex],
max_length=17, blank=True, null=False, unique=True) # validators
should be a list
linkedin_url = models.URLField(max_length=255, unique=True,
blank=True, null=True) #make sure diff users cant use two same
profile
twitter_url = models.URLField(max_length=255, unique=True) #make
sure diff users cant use two same profile
articles = ArrayField(models.URLField(), blank=False, null=False,
unique=True, size=3)
country = models.ForeignKey(Country, on_delete=models.CASCADE,
blank=False, related_name="applicant")
category = models.ForeignKey(Category, on_delete=models.CASCADE,
related_name="applicant", blank=False)
status = models.CharField(max_length=200,
choices=APPLICATION_STATUS, default=1)
def __str__(self):
return self.first_name
class User(AbstractUser):
USER_TYPE_CHOICES = (
(1, 'Journalist'),
(2, 'Admin'),
)
GENDER = (
(1, 'Male'),
(2, 'Female')
)
first_name = models.CharField(max_length=200, blank=False)
last_name = models.CharField(max_length=200, blank=False)
# is_active = models.BooleanField(default=True)
password = models.CharField(max_length=200)
email = models.EmailField(max_length=250, unique=True)
phone_regex = RegexValidator(regex=r'^\+?1?\d{9,15}$',
message="Phone number must be entered in the format:
'+999999999'. Up to 15 digits allowed.")
phone_number = models.CharField(validators=[phone_regex],
max_length=17, unique=True, blank=False) # validators should be a
list
user_type = models.CharField(max_length=200,
choices=USER_TYPE_CHOICES, default=1)
category = models.ForeignKey(Category, on_delete=models.CASCADE,
related_name="users")
# posts_url = ArrayField(models.URLField(), size=3)
country = models.ForeignKey(Country, on_delete=models.CASCADE,
blank=True, related_name="users")
gender = models.CharField(max_length=200, choices=GENDER)
image_url = models.URLField(max_length=255)
about = models.TextField()
applicant = models.ForeignKey(Applicant,
on_delete=models.CASCADE, blank=True, null=True,
related_name="users")
def __str__(self):
return self.username
class Meta:
ordering = ("-date_joined",)
views.py:
class IdentifyUserApplicantID(generics.RetrieveAPIView):
queryset = User.objects.all()
serializer_class = ApplicantSerializer
permission_classes = (IsAuthenticated,)
# def get_queryset(self, pk):
# return Applicant.objects.get(id=pk)
def get(self, request):
data = request.data.get('pk')
admin_user = User.objects.get(user_type=2)
if request.user == admin_user:
try:
queryset = User.objects.get(applicant=data)
serializer = UserSerializer(queryset)
return Response(jsend.success({'users':
serializer.data}))
except User.DoesNotExist:
return Response(jsend.success({'users': '[]'}))
else:
return Response((jsend.error("You are not authorized to
perform this action")),
status=status.HTTP_404_NOT_FOUND)
P.S: I checked out django-filter module but the docs isn't clearly written and a bit hard to understand. Would be glad if more clarification is made in that regard if django-filter is the recommended way of implementing the solution.
I don't know if I understand your question, but do you mean something like this:
if User.objects.filter(applicant__id=pk).exists():
path('api/v1/users', views.YOURVIEW.as_view())
and in the view
applicantId = request.GET.get('applicant', None)
The key was to filter against query parameter, which was done here: http://www.django-rest-framework.org/api-guide/filtering/#filtering-against-query-parameters
Using request.query_params.get() to get the users query via URL in your .get_queryset() method...and pass it as your queryset when creating a response object.
My updated views.py:
class IdentifyUserApplicantID(generics.ListAPIView):
serializer_class = UserSerializer
permission_classes = [IsAuthenticated,]
queryset = User.objects.all()
def get_queryset(self):
queryset = User.objects.all()
applicant = self.request.query_params.get("applicant", None)
if applicant is not None:
queryset = queryset.get(applicant=applicant)
return queryset
def get(self, request):
if request.user.user_type == "2":
try:
queryset = self.get_queryset()
serializer = UserSerializer(queryset)
return Response(jsend.success({'user': serializer.data}))
except User.DoesNotExist:
return Response(jsend.success({'user': '[]'}))
else:
return Response((jsend.error("You are not authorized to perform
this action")),status=status.HTTP_403_FORBIDDEN)
My updated urls.py
re_path(r'^users$', IdentifyUserApplicantID.as_view(), name="identify-
applicant"),
Example Query:
http://example.com/api/v1/users?applicant=2
Hope this helps. Thanks everyone for your answers.

Categories

Resources