Value Error. Cannot assign query set. It must be an instance - python

I want to create object in my comment model.
It's my Reply model to a post.
class Reply(MPTTModel):
post = models.ForeignKey(Post, on_delete=models.CASCADE)
detail = models.CharField(max_length=50, null=True, blank=True, unique=True)
pub_date = models.DateTimeField(auto_now_add=True)
last_edited = models.DateTimeField(auto_now=True)
author = models.ForeignKey(User, on_delete=models.CASCADE)
class MPTTMeta:
order_insertion_by = ['name']
It's my view function for reply.
def post_detail(request,id,**kwargs):
post = Post.objects.all().filter(id=id)
comment = CommentReply.objects.filter(post=id)
context = {'post': post, 'comment': comment}
if request.method == 'POST':
comments = request.POST.get('comment')
Reply.objects.create(
post=post, detail=comments, author = request.user)
else:
return render(request, 'khalidblog_app/full_detail.html', context)
return render(request,'khalidblog_app/full_detail.html',context)
I'm using for loop in my template:
{% for post in post %}
<img alt='' src='{{ post.image.url }}' class='avatar avatar-80 photo' height='80' width='80' />
<div class="eltd-author-description-text-holder">
<h5 class="eltd-author-name"> {{post.author}} </h5>
<div class="eltd-author-text">
<p>{{post.title}}</p>
</div>
<div class="eltd-author-text">
<p>{{post.content}}</p>
<hr>
</div>
{%endfor%}
When I run this view function It shows a value error as follows.
Cannot assign "<QuerySet [<Post: Author : zorainTiTleFirst PostDated :2021-01-17 16:27:47.043869+00:00>]>": "Reply.post" must be a "Post" instance.
How I can set the forignkey(Post) in this reply model using this view function?

On the very first line of your function, you are using .filter() method which returns QuerySet (List of Objects in short), where you should have ideally used .get() method.
.get() returns a single object which the assignment on line 6 actually expects.

Your 'post' variable is pointing to a QuerySet of Post objects, rather than a single instance. You should use get() instead of filter() as follows:
post = Post.objects.get(id=id)
Update: You shared in the comments that you were using a for loop in your template. A for loop is designed for iterating over a group of objects (e.g. a QuerySet that would be returned by using a filter()). If you just want to render the values of fields for a single instance, you do not need to use a for loop.
Here is a link to the Django docs, for filter() - which returns a QuerySet (group of objects).
Here is a link to the Django docs, for get() - which returns single object.

since your 'post' variable is pointing to a QuerySet of Post objects you can directly select the first element in the QuerySet as like this :
Reply.objects.create(
post=post[0], detail=comments, author = request.user)

Related

Foreign key POST in Django

I have some problems with my app . I need to make task with relations to user.
Here is my code:
bug
ValueError at /
Cannot assign "(<SimpleLazyObject: <function AuthenticationMiddleware.process_request.<locals>.<lambda> at 0x04C54E38>>,)": "Task.usertask" must be a "UserMembership" instance.
models.py
class UserMembership(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.PROTECT, null=True,blank=True)
image=models.FileField(upload_to='photos/%Y/%m/%d/',null=True,blank=True)
class Task(models.Model):
title=models.CharField(max_length=200)
date = models.DateTimeField(default=datetime.now,blank=True)
is_published=models.BooleanField(default=True)
usertask=models.ForeignKey(UserMembership, on_delete=models.PROTECT, null=True, blank=True)
views.py
if request.method == 'POST':
usertask = request.user,
title = request.POST['title']
task = Task(usertask=usertask,title=title)
task.save()
task = Task.objects.order_by('-date').filter(is_published=True, usertask=request.user.id)
context={
'task':task,
}
return render(request,'index.html',context)
html form
<form action="" method="POST">
{% csrf_token %}
<input name="title" type="text" class="form-control" id="usr">
<button type="submit" class="btn btn-primary">add</button>
Your task model's usertask field related with UserMembership model as ForeignKey.So you must give a UserMembership object for create Task object. You gave request.user as usertask but request.user is'nt a UserMembership object.You must find UserMembership object before. You can use following code:
user_member_ship = UserMemberShip.objects.get(user=request.user)
Task.objects.create(title=title,usertask=user_member_ship)
A few things:
1) in your views function you are doing this: usertask = request.user. Note that request.user will return a User object.
2) You then try to create a Task object by doing this task = Task(usertask=usertask,title=title) but here you have set usertask to a User object when you really need a UserMembership object
You can try rewriting your view function like so:
def MyViewFunction(request):
# unpack request:
user = request.user
title = request.POST['title']
# create new UserMembership object:
user_membership = UserMembership.objects.create(user=user)
user_membership.save()
# create new Task object:
task = Task.objects.create(usetask=user_membership, title=title)
task.save()
# continue code here...
As the error says your usertask field of your Task model refers to a UserMembership, not a User object.
You can use .get_or_create() [Django-doc] to retrieve, or create such object if it does not yet exists. For example:
def myview(request):
if request.method == 'POST':
usertask, __ = UserMembership.objects.get_or_create(
user=request.user
)
title = request.POST['title']
task = Task.objects.create(usertask=usertask, title=title)
task = Task.objects.order_by('-date').filter(
is_published=True,
usertask__user=request.user
)
context={
'task':task,
}
return render(request,'index.html',context)
You should filter on the usertask__user=request.user, since again, a usertask will retrieve a UserMembership object, not a User object.
I strongly advice to work with forms [Django-doc] and not try to process the request.POST querydictionary yourself. It is for example possible that the POST request does not contain any value for title, which can raise errors. A form will handle that more elegantly.

How do I avoid `obj.save()` not to update `updated_at` field?

The title alone seems to be vague. Here is the detail.
I have the following Model:
class Post(models.Model):
title = models.CharField(max_length=100)
# deleted for brevity
created_at = models.DateTimeField(blank=True, null=True, auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
count_visits = models.PositiveIntegerField(default=1) # counts visit time of posts
and added the following code in blogs.html:
{% if post.created_at == post.updated_at %}
Posted {{ post.created_at|naturaltime }}
{% else %}
Updated {{ post.updated_at|naturaltime }}
{% endif %}
then I made a simple if statement in DetailView to keep track of number of visitors of a certain post:
def post_detail(request, title_slug):
obj = get_object_or_404(Post, slug=title_slug)
session_key = f'key_{title_slug}'
if not request.session.get(session_key, False):
obj.count_visits += 1
obj.save()
request.session[session_key] = True
return render(request, 'blogApp/detail.html', {'post': obj})
Here is the problem, When I create a post, in list view it shows Posted now.
but as I click the post to view its detail. It shows Updated now
and I think I know what is the problem, when I go to detail view. it creates a session_key, increments obj.count_visits += 1 and save the object obj.save().
When obj.save() is called it updates the database including updated_at field and that is why I get Updated now.
How do I solve this? I want to increment number of count_views field, but when object is saved it shouldn't change updated_at field.
I hope you help me. Thank You
You can do the update directly on the database, bypassing the Django layer:
from django.db.models import F
if not request.session.get(session_key, False):
type(obj).objects.filter(pk=obj.pk).update(
count_visits=F('count_visits') + 1
)
This will result in the following DB query:
UPDATE "<db_table>" SET "count_visits" = ("<db_table>"."count_visits" + 1) \
WHERE "<db_table>"."id" = <obj.id>
The F-object is used to resolve references to existing database objects, in this case, we're getting the current value of field count_visits. The important property of this is that it can generate expression from the operation and operands (objects) by implementing e.g. __add__, __sub__, __mul__, __div__ and other dunder methods (actually implemented by its superclass Combinable).
Another approach would be to take an additional keyword argument to save that indicates whether the updated_at field should be updated as well. To implement this, at first we need to drop the auto_now argument from the field instantiation as well:
from django.utils import timezone
class Post(models.Model):
...
updated_at = models.DateTimeField(default=timezone.now)
...
def save(self, *args, set_updated_at=True, **kwargs):
if self.pk is not None:
if set_updated_at:
self.updated_at = timezone.now()
super().save(*args, **kwargs)
Now you only need to pass the argument when calling save e.g. in this case, it should be False:
if not request.session.get(session_key, False):
obj.count_visits += 1
obj.save(set_updated_at=False)

Django view only returning incomplete data, only one field to template

I have forked the django-oscar catalogue app to alter the models being used. Not in a major way, and not in a way that would affect pulling data from the database as far as I can see. This seems to be supported by the fact the the django-oscar dashboard still works fine and lets me add and view products. My models.py from my forked app:
from django.db import models
class Collection(models.Model):
name = models.CharField(max_length=50)
prod_category = models.CharField(max_length=50)
description = models.TextField()
manufacturer = models.TextField()
num_products = models.PositiveIntegerField()
image_url = models.URLField()
from oscar.apps.catalogue.abstract_models import AbstractProduct
class Product(AbstractProduct):
collection = models.ForeignKey(Collection, on_delete=models.CASCADE, null=True)
multiplier = models.DecimalField(max_digits=2, decimal_places=1, default='2.2')
from oscar.apps.catalogue.models import *
Here is my relevant view from my views.py
def product(request):
template = loader.get_template('/home/my_app/my_site/main_page/templates/main_page/product.html')
prods = Product.objects.values_list('categories')
context={'prods': prods}
return HttpResponse(template.render(context))
I tried loading from the built in model and my forked model (commenting and uncommenting one or both), neither makes a difference:
#from forkedoscarapps.catalogue.models import Product
from oscar.core.loading import get_class, get_model
Product = get_model('catalogue', 'product')
And the code I am using in the template to display data from the view:
{% for instance in prods %}
<li><{{ instance.name }}</li>
{% endfor %}
The resulting HTML is:
<li></li>
Which shows it is reaching the for loop, but for some reason no data is returned.
There is at least one category called beds, which displays fine in the django-oscar dashboard. What have I missed in my view?
edit: When I change instance.name to just instance I get the following returned in the HTML:
(1,)
So it is somewhat working, and showing what I assume is the primary key being returned, but why is the name of the field not being returned?
Product.objects.values_list('categories') yields a list of id tuples that represent the categories associated with the products in that queryset. That's not what you want to send to the template, you want to send instances, more specifically product instances if I'm not mistaken.
Do Product.objects.all() instead, and just use {{ instance.title }} in the template according to the definition of the oscar model: https://github.com/django-oscar/django-oscar/blob/master/src/oscar/apps/catalogue/abstract_models.py and to what ever you customised over it.

Django "for" loop by date

I'm doing blog app. I did:
{% for entry in entry.all %}
<div class="timelinestamp">
...
</div>
<br />
<br />
<br />
<br />
{% endfor %}
and almost everything works fine. I changed one Entry in my admin panel (The very first Entry...). Since then the order of my post has changed... Can anyone explain me why ? Or tell how to using loop render all Entries sorted by date ?
class Entry(models.Model):
title = models.CharField(max_length=120)
pub_date = models.DateField(null=False)
body = models.TextField()
image = models.ImageField(upload_to='images/', max_length = 100)
def __str__(self):
return self.title
The pub_date field is NOT primary key in my DB! I'm using Django 2.1
From the docs:
If a query doesn’t have an ordering specified, results are returned from the database in an unspecified order. A particular ordering is guaranteed only when ordering by a set of fields that uniquely identify each object in the results. For example, if a name field isn’t unique, ordering by it won’t guarantee objects with the same name always appear in the same order.
It looks like you want to order by pub_date? Use ordering:
class Entry(models.Model):
...
class Meta:
ordering = ['-pub_date']
If you have created model definition without the ordering meta option items in the database are not enforced any ordering, everytime when you do Model.objects.all() it will give you items without any order. If you want to queries to be in specific order you can:
Add ordering option to Meta options to model definition - which would require
database migrations
Modify your query to enforce ordering like Model.objects.all().order_by('-pub_date') - also pass the query as context object to template like:
views.py -
entries_by_pub_date = Model.objects.all().order_by('-pub_date')
context['entries_by_pub_date'] = entries_by_pub_date
template
{% for entry in entries_by_pub_date %}
...
{% enfor %}
As far as I can see you haven't defined a sort order for your Entry model. This means that you will process those entries in a non-defined order.
To order your entries you could set a default sort order on Entry:
class Entry(models.Model):
title = models.CharField(max_length=120)
pub_date = models.DateField(null=False)
body = models.TextField()
image = models.ImageField(upload_to='images/', max_length = 100)
def __str__(self):
return self.title
class Meta:
ordering = ('-pub_date',)
Or, if that's not what you're looking for, you could order your queryset in your view:
Entry.objects.all().order_by('-pub_date')

Django Apply Model Foreign Key Default Value to Existing Record

Thank you very much for taking your time.
Previously, I posted this question, How to Get Unread Posts for Users.
The problem was: I cannot filter out which article one user has not read while this article has already been read by another user.
I figured out why I cannot do that---- because I have data that I wrote into the database without using Django although I set the reading default for each post to False---- there is simply no record.
Then, I manually set one article to unread to a user in the Admin, everything works, because now in the database there is one record stating that this certain article has not been read by this user.
The problem now is:
How do I set "unread" for every existing article that I have in the database for all existing users?
And how can articles stay unread for every new user unless the new user actually read it?
For your convenience, I copied the codes to here.
My model:
class Posts(models.Model):
title = models.CharField(max_length=255)
content_url = models.URLField(unique=True)
content = models.CharField(max_length=255)
post_date = models.DateField(default="2999-12-12")
Another
class readstatus(models.Model):
reading_status = models.BooleanField(default=False)
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
article = models.ForeignKey(Posts, on_delete=models.CASCADE)
created = models.DateTimeField(auto_now_add=True)
My View:
class DailyReading(ListView):
template_name = 'reading.html'
context_object_name = "data"
paginate_by = 20
def get_queryset(self):
if self.kwargs['status'] == "read":
queryset = piaoyou.objects.filter(readstatus__reading_status=True,readstatus__user=self.request.user)
return queryset
else:
queryset= Posts.objects.filter(readstatus__reading_status=False,readstatus__user=self.request.user)
return queryset
My Template:
{% for info in data %}
<quoteblock>
<ul>
<li><a href="{{ info.get_absolute_url }}">{{ info.title }}
<footnote></footnote>
</a>{{ info.post_date }}</li>
<footnote>{{ info.get_abstract_content }}</footnote>
</ul>
</quoteblock>
{% endfor %}
OMG, I just figured out how to do this.
For an article that has been read by the requesting user, we can just pass queryset = Post.objects.filter(readstatus__reading_status=True, readstatus__user=self.request.user)
For an article that has not been read by the requesting user yet, we can pass Posts.objects.all() into the template.
Then in the template:
We need {% if instance.readstatus_set.all %} this line to make things work. Assume there is no data about whether this requesting user has read the article, the instance should not have readstatus_set.all, which means the user has not read the article yet. Once checking this if condition, we can carry out to check other conditions in the loop.

Categories

Resources