I have two models as Author and Comment
One is Comment and it has a ForeinKey points to Author(BlogUser)
class Comment(models.Model):
body = models.TextField('body', max_length=500)
created_time = models.DateTimeField('create_time', default=now)
last_mod_time = models.DateTimeField('last_mod_time', default=now)
author = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name='author', on_delete=models.CASCADE)
article = models.ForeignKey(Article, verbose_name='article', on_delete=models.CASCADE)
parent_comment = models.ForeignKey('self', verbose_name="parent_comment", blank=True, null=True, on_delete=models.CASCADE)
is_approved = models.BooleanField('is_approved', default=True, blank=False, null=False)
is_enabled = models.BooleanField('is_enabled', default=True, blank=False, null=False)
is_deleted = models.BooleanField('is_deleted', default=True, blank=False, null=False)
Author Model
class BlogUser(AbstractUser):
qq_number = models.CharField('QQ', max_length=20, default='')
I am trying to get all the comments related to a specified Article and display the comments list by the format of usernames and comment bodys.
In the view I filter all the comments by the article id, but after serializing the filter queryset, it gives me only the Author's primary id, how could I get the other feilds of Author? Shall I write a function by myself or there is some easier way to do it?
The view:
def get_comment_list(request, article_id):
if request.GET:
comment_list = models.Comment.objects.filter(article_id = article_id)
data = serializers.serialize('json',comment_list)
return HttpResponse(data, content_type="application/json")
else:
PermissionDenied('Not accept post')
You need to iterate the comment_list and with the member operator to access the fields of author
eg: comment_list[0].author.field_name
NOTE: Field name could be any field name that has been decleared in AUTH_USER_MODEL table like first_name, last_name or username.
By default, django serializer does not provide information from ForeignKey. I would also recommend using Django Rest Framework Serializer(Denial Roseman mentioned in the comments) if you have the scope of using it.
Still you can use the django serializer, but you will need to override the json.Serializer's end_object() method. There you canget the values of the user object like following example:
from django.core.serializers.json import Serializer
class CustomSerializer(Serializer):
def end_object(self, obj):
for field in self.selected_fields:
if field in self._current.keys():
continue
else:
try:
self._current[field] = getattr(obj.author, field) # Fetching the data from ForeignKey
except AttributeError:
pass
super(CustomSerializer, self).end_object(obj)
# usage
data = CustomSerializer().serialize(
Comment.objects.all(),
fields = (
'pk',
# other fields in Comment Object
'username' # it belongs to Author Model
)
Downside of this solution is that, if BlogUser has same field as Comment, then it will fetch the value of Comment.
More Advanced Implementation:
class CustomSerializer(Serializer):
def end_object(self, obj):
for field in self.selected_fields:
if field in self._current.keys():
continue
else:
if field.find('__') > -1:
try:
fks = field.split('__')
curr_value = obj
while(len(fks)>0):
current_key = fks.pop(0)
curr_value = getattr(curr_value, current_key)
self._current[field] = curr_value
except AttributeError:
pass
super(CustomSerializer, self).end_object(obj)
# usage
data = CustomSerializer().serialize(
Comment.objects.all(),
fields = (
'pk',
# other fields in Comment Object
'author__username' # it belongs to Author Model
)
Related
I am building a Blog App and I am trying to sort or order_by in list which contains multiple queries.
models.py
class BlogPost(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=30)
date = models.DateTimeField(auto_now_add=True)
class Comments(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
blog_of = models.ForeignKey(BlogPost, on_delete=models.CASCADE)
body = models.CharField(max_length=30, default='')
date_added = models.DateTimeField(auto_now_add=True)
views.py
def mypage(request):
query_1 = list(BlogPost.objects.filter(user=request.user).order_by('-date'))
query_2 = list(Comment.objects.filter(user=request.user).order_by('date_added'))
results = sorted(chain(query_1, query_2),key=attrgetter('date') , reverse=True)
context = {'results':results}
return render(reques, 'mypage.html', context)
But is showing
'Comment' object has no attribute 'date'
And I think this is because date field name is different in both model and i am sorting with only one, But i have no idea how can I sort with different field name.
Any help would be much Appreciated. Thank You
Or just add it as a property:
class Comments(models.Model): # do NOT give a model a plural name!
# ....
#property
def date(self):
return self.date_added
# or if it is a datetimefield
# return self.date_added.date()
ALso you can just write a more customized sorting key (e.g. in case the involved models are from third-party apps).
def order(obj):
try:
return obj.date
except AttributeError:
return obj.date_added
# ...
results = sorted(chain(query_1, query_2), key=order, reverse=True)
I am working on a small application containing models CustomUser and PollQuestion. CustomUser having ManyToMany relation between itself and a CustomUser can have multiple PollsQuestion as well so there is Foreign Key relation between them.
An authenticated user is only able to see polls raised by users he is following, to full-fill this requirement i have written following view**:-**
Actually this is not view this is an utility method returning the polls to original view.
def all_polls_utils(request):
following = request.user.get_all_followings().values_list("id")
user_id = [id[0] for id in following]
all_polls = PollQuestion.objects.none()
for u_id in user_id:
user = CustomUser.objects.get(id=u_id)
polls = user.poll_questions.all()
all_polls = all_polls | polls
return all_polls
Main Question:- Is there in better way to do the same?
Any suggestion will be highly appretiated
I am posting the models bellow:-
from django.db import models
from django.contrib.auth.models import AbstractUser
# Create your models here.
class CustomUser(AbstractUser):
email = models.EmailField(max_length=250, null=False)
name = models.CharField(max_length=50, null=False)
username = models.CharField(max_length=50, null=False, unique=True)
password = models.CharField(max_length=15, null=False)
user = models.ManyToManyField('self', through='Relationship', symmetrical=False, related_name='related_to')
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['name', 'email']
def get_all_polls(self):
pass
def create_relationship(self, person):
status, obj = Relationship.objects.get_or_create(
to_person=person,
from_person=self,
)
return status
def remove_relationship(self, person):
Relationship.objects.filter(
from_person=self,
to_person=person
).delete()
return 'dummy_value'
def get_all_followings(self):
return self.user.all()
class Relationship(models.Model):
from_person = models.ForeignKey(CustomUser, related_name='from_people', on_delete=models.CASCADE)
to_person = models.ForeignKey(CustomUser, related_name='to_person', on_delete=models.CASCADE)
And PollQuestion:-
class PollQuestion(models.Model):
user = models.ForeignKey(CustomUser, null=True, on_delete=models.CASCADE, related_name="poll_questions")
# Other fields
Note:- You can also suggest me a better title for this post?
Thanks in advance,
Hope to here from you soon.
Simply
def all_polls_utils(request):
all_polls_by_followed = PollQuestion.objects.filter(
user__in=request.user.get_all_followings()
)
As an aside, you should probably rename the user many-to-many in CustomUser to e.g. followed_users (with a related name followers).
In my view I do following:
class ReviewViewSet(viewsets.ModelViewSet):
#queryset, serializer_class and permission_classes defined here
def perform_create(self, serializer):
title_id = self.kwargs.get('title_id')
title = get_object_or_404(Title, pk=title_id)
serializer.save(author=self.request.user, title=title)
I want to validate that review doesn't exist yet. I'm trying to do this in serializer's validate():
class ReviewSerializer(serializers.ModelSerializer):
title = serializers.SlugRelatedField(slug_field='pk', read_only='True')
author = serializers.SlugRelatedField(slug_field='username', read_only='True')
def validate(self, data):
title = # <-- How to get title here?
author = self.context['request'].user
queryset = Review.objects.all().filter(title=title, author=author)
if queryset.exists():
raise serializers.ValidationError('Review alredy exists')
return(data)
class Meta:
fields = '__all__'
model = Review
Attempt to do title = self.title raises AttributeError: 'ReviewSerializer' object has no attribute 'title'
How to access title from inside validate() in my case?
Here's my Review model:
class Review(models.Model):
class Meta:
ordering = ['-pub_date']
unique_together = ['title', 'author']
title = models.ForeignKey(
Title,
on_delete=models.CASCADE,
related_name='reviews',
)
author = models.ForeignKey(
CustomUser,
on_delete=models.CASCADE,
related_name='reviews',
)
text = models.TextField('Review text')
score = models.PositiveSmallIntegerField(
'Review score',
validators=[
MinValueValidator(1),
MaxValueValidator(10)
]
)
pub_date = models.DateTimeField(
'Date and time of review',
auto_now_add=True,
db_index=True
)
The response to your question lies in the documentation : https://www.django-rest-framework.org/api-guide/serializers/#object-level-validation
About the validate method:
This method takes a single argument, which is a dictionary of field values
If you look at the code sample in the doc, you'll see that all data that you might need to validate are in the data argument of the validate method.
Hence, the title is in data['title'].
It's my first time creating a Django website with models, and in my first attempt to insert data into my table I'm getting this error.
My models are as follows:
class User(AbstractUser):
pass
#https://docs.djangoproject.com/en/3.1/topics/auth/default/
class Listing(models.Model):
listingID = models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="listID")
created_by = models.ForeignKey(User, on_delete=models.SET_NULL, related_name="myListing", null=True)
watchers = models.ManyToManyField(User, blank=True, related_name="watchlist")
title = models.CharField(max_length=30)
description = models.TextField()
creation_date = models.DateField(auto_now=True)
img_url = models.URLField()
active = models.BooleanField(default=True)
def __str__(self):
return f"{self.title}"
class Bid(models.Model):
listing = models.ForeignKey(Listing, on_delete=models.SET_NULL, related_name="bidsMadeOnMe", null=True, blank=True)
user = models.ForeignKey(User, on_delete=models.SET_NULL, related_name="myBids", null=True)
price = models.FloatField()
creation_date = models.DateField(auto_now=True, null=True)
def __str__(self):
return f"Bid={self.price}"
and the view that handles the form submission is this one:
#login_required
def create_listing(request):
if request.method == "POST":
user = User.objects.get(username=request.user.username)
l = Listing(created_by=user,
title=request.POST["title"],
description=request.POST["desc"],
# https://stackoverflow.com/questions/12176585/handling-dates-over-request-get
creation_date=models.DateTimeField(auto_now_add=True),
img_url=request.POST["image_url"]
)
l.save()
b = Bid(l,
user,
request.POST["initial_bid"],
models.DateTimeField(auto_now_add=True)
)
b.save()
return render(request, "auctions/index.html")
I know the problem is the way I'm adding the data but I can't fix it. Can someone give me some light?
Your problem (well, several actually) is this:
b = Bid(l, user, request.POST["initial_bid"], models.DateTimeField(auto_now_add=True))
You're constructing a model instance by positional arguments instead of keyword arguments. This can be done, but then the invisible "id" column that has been added to the Bid model, is the first argument.
In Django we never construct models like that, but always use keyword arguments, so we're not depending on field order:
b = Bid(listing=l, user=user, ...))
Once you're solved that, your next problem is the date field.
Don't assign fields to model instances. Fields are class declarations, they don't belong on instances. Fields describe on a class (= a Model), what kind data to expect. On the instance, you assign that data.
In this case, your definition for the field is wrong on the model and on the instance you shouldn't even assign it - it will be automatically filled.
Overall, it feels like you haven't gone through Django's tutorial or did not fully understand the concepts. I suggest you go through it.
I have a ManyToMany relationship with one of my Models. On deleting a child, I want to remove the relationship but leave the record as it might be being used by other objects. On calling the delete view, I get an AttributeError error:
Exception Value: 'QuerySet' object has
no attribute 'clear'
This is my models.py:
class Feed(models.Model):
username = models.CharField(max_length=255, unique=True)
class Digest(models.Model):
name = models.CharField(max_length=255)
user = models.ForeignKey(User)
items = models.PositiveIntegerField()
keywords = models.CharField(max_length=255, null=True, blank=True)
digest_id = models.CharField(max_length=20, unique=True)
time_added = models.DateTimeField(auto_now_add=True)
active = models.BooleanField(default=1)
feeds = models.ManyToManyField(Feed)
And the relevant section of views.py:
def feed_delete(request, id):
digest = get_object_or_404(Digest, id=id)
if digest.user == request.user:
Feed.objects.get(id=request.POST.get('id')).digest_set.filter(id=id).clear()
return HttpResponseRedirect(digest.get_absolute_url())
Clear the fields on a Digest isntance
digest = get_object_or_404(Digest, id=id)
if digest.user == request.user:
digest.feeds.clear()
#do your processing
In response to your comment.
digest = get_object_or_404(Digest, id=id)
if digest.user == request.user:
feed=digest.feeds.get(id=2)#get an instance of the feed to remove
digest.feeds.remove(feed)#remove the instance
Hope this helps!