I have a problem with my django app, i'm created two Blog objects, one in mysql and another one with my view. I see them both in my database but i can't see them when i get them with a query.
I'm using django 1.9, python 2.7, apache 2.2 with mod_wsgi
Here is my view and my template.
def list_view(request):
blogs = Blog.objects.filter(published=True)
return render_to_response('blog/list.html',
{
"blogs": blogs,
},
context_instance=RequestContext(request))
{% for blog in blogs %}
<h2>{{blog.title}}</h2>
<p>{{blog.content}}</p>
<span>{% trans 'Writen by' %} : {{blog.writer.last_name}} {{blog.writer.first_name}}</span> - <span>{% trans 'Published on' %} {{blog.date_published}}</span>
{% endfor %}
The query gets me a list with 2 Blog objects inside, but they are empty. My template just shows Writen By - Published on twice.
But i can log in and print all my user informations.
Any idea what could be the problem or how i could solve it ? Thanks a lot !
EDIT : add models.py
class Blog(models.Model):
title = models.CharField(_("Title"), max_length=255, null=False, blank=False)
content = models.TextField(_("Content"), null=True, blank=True)
writer = models.ForeignKey(User, verbose_name=_('Writer'), blank=False, null=False)
published = models.BooleanField(_("Pusblished"), default=False)
date_published = models.DateTimeField(_("Date published"))
def __str__(self):
return '%s' % (self.title)
def __init__(self, *args, **kwargs):
super(Blog, self).__init__()
When you overrode Blog.__init__(), you did not send *args and **kwargs to the parent. Django Model magic needs those parameters. If you're not doing anything in __init__() you could just remove that all together.
If you want to keep it, do something like:
def __init__(self, *args, **kwargs):
super(Blog, self).__init__(*args, **kwargs)
Related
I am building an e-commerce website with django. There is a homepage which lists all items on website and when the user click on any of those items they will direct to details page which shows details about that item. Also on that detail page, I added a "wishlist" button so that users can add that item to their wishlist. My problem is, if the user does not have that item in their wishlist, I want to display "Add to wishlist" button to add that item to their wishlist when they clicked on it, otherwise I want to display "remove from wishlist" button to remove the item from their wishlist when they clicked on it. I have two separate function to add and remove items and they perfectly work but I want to display only one of the buttons "add" or "remove" by checking if that item exists in their wishlist or not. So my logic was writing a function checks if the user has that item in their wishlist or not in DetailView which displays the "detail" page in views.py and display only one of them but I couldn't.
This is views.py
class ItemDetailView(FormMixin, DetailView):
model = Auction
form_class = CommentForm
template_name = "auctions/details.html"
def dispatch(self, request, *args, **kwargs):
listing = get_object_or_404(Auction, pk=request.user.id)
if_exists = Wishlist.objects.filter(user=request.user, item=listing).exists()
return super().dispatch(request, if_exists, *args, **kwargs)
.
.
.
.
.
This is models.py
class Auction(models.Model):
title = models.CharField(max_length=64)
price = models.IntegerField()
created = models.DateTimeField(auto_now_add=True)
image = models.ImageField(null=True, blank=True, upload_to='images/')
category = models.ForeignKey(Category, on_delete=models.CASCADE, default="")
owner = models.ForeignKey(User, on_delete=models.CASCADE, default="")
description = models.TextField(max_length=256)
def __str__(self):
return f"({self.id}: {self.title} , {self.price}, {self.owner})"
def get_absolute_url(self):
return reverse('item-detail', args=[str(self.id)])
class Wishlist(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
item = models.ForeignKey(Auction, on_delete=models.CASCADE, null=True)
def __str__(self):
return f"{self.item}"
This is urls.py
path("item/<int:pk>", ItemDetailView.as_view(), name="item-detail"),
And I think the html should be something like this because the function should return True or False.
{% if not if_exists %}
</li style="display:inline-block">
Add To Wishlist
</ul>
{% else %}
</li style="display:inline-block">
Remove From Wishlist
</ul>
{% endif %}
So my logic was calling a function in DetailView when the user wants to see the details of items.
Result is there is no error but html doesn't show the button "add" when it should do so probably this func is returning None or False.(or not even working) but not True.
I am a newbie in software engineering so my code can be odd. I am open to any logics or suggestions. Thanks in advance.
I am trying to add the no. of comments related to a post in my Django Project. but I keep receiving a 'Post' object has no attribute 'comment_set' AttributeError for some reason I don't understand why.
My project has a Post Model
class Post(models.Model):
title = models.CharField(max_length=100, unique=True)
----------------------------------------
# To know how many comments
def num_comments(self):
return self.comment_set.all().count() <--------- Error from here
class Comment(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="commented_users")
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name="commented_posts")
content = models.TextField(max_length=160)
updated = models.DateTimeField(auto_now=True)
created = models.DateTimeField(auto_now=True)
Here is sthe views.py
class UserOnlyPostListView(ListView):
model = Post
template_name = "score/user_only_posts.html"
context_object_name = 'posts'
paginate_by = 4
def get_queryset(self):
user = get_object_or_404(User, username=self.kwargs.get('username'))
return Post.objects.filter(designer=user).order_by('-date_posted')
def get_context_data(self, *args, **kwargs):
context = super(UserOnlyPostListView, self).get_context_data()
user = get_object_or_404(User, username=self.kwargs.get('username'))
return context
Here is the template:
{% for post in posts %}
<td>{{ post.num_comments }}</td>
{% endfor %}
My question:
Why am I receiving this error and how to fix it?
Thank you
comment_set is the default related_name django gives to get all of the comments that point towards that particular instance of Post.
However, you have set related_name="commented_posts", which means the default value of comment_set is overwritten, and you should use post.commented_posts rather than post.comment_set.
Note: It might be worth using a different related name as post.commented_posts is a bit confusing (as it's returning a set of Comments not Posts). A related name of comments would be better. That way you would use post.comments.
instead of
return self.comment_set.all().count()
you have to use
return self.commented_posts.all().count()
from your
post = models.ForeignKey(Post, on_delete=models.CASCADE,
related_name="commented_posts")
if you delete related_name then you go with comment_set.
I've made a simple test django project that's a simple list of posts. And I'm trying to add functionality to update a post. Everything seems to be working, except the edits aren't saved to the database.
I've checked the cleaned data to see if the updated data is coming through, and it is, but the save() function doesn't seem to actually do anything.
models.py
class Block(models.Model):
title = models.CharField(max_length=255)
slug = models.SlugField(unique=True, max_length=140, null=True, blank=True)
content = models.TextField()
def save(self, *args, **kwargs):
if self.slug is None:
self.slug = get_unique_slug(self, 'title', 'slug')
super().save(*args, **kwargs)
def get_absolute_url(self):
return reverse("bulkapp:one_view", kwargs={"slug_id": self.slug})
def __str__(self):
return self.title
views.py
def edit_block_view(request, slug_id):
single_block_query = get_object_or_404(Block, slug=slug_id)
update_form_query = BlockForm(request.POST or None, instance=single_block_query)
if update_form_query.is_valid():
update_form_query.save()
return redirect('bulkapp:one_view', slug_id=slug_id)
return render(request, 'bulkapp/update.html', {'update_form': update_form_query})
<form class="form-container" method="POST">
{% csrf_token %}
{{update_form.as_p}}
<input type="submit" value="Edit">
</form>
Edit:
forms.py
class BlockForm(forms.ModelForm):
class Meta:
model = Block
fields = [
"title",
"slug",
"content",
]
The redirect fires as expected, but no changes are saved, and no error messages are written to the console. Any help would be much appreciated.
The problem was the indentation of the super().save(*args, **kwargs) in my save() function which didn't execute if it the slug was not null.
I'm working on a fairly simple library project in Django, here are my models, a book can have many copies and each copy can have many loans.
class Author(models.Model):
name = models.CharField(max_length=200, unique=True)
class Book(TimeStampedModel):
isbn = models.CharField(max_length=13, primary_key=True)
title = models.CharField(max_length=200, db_index=True, unique=True)
authors = models.ManyToManyField('Author', related_name='books')
#property
def is_available(self):
"""Returns True if the Book has any available copies"""
return self.copies.exclude(loans__returned=False).exists()
class BookCopy(models.Model):
book = models.ForeignKey('Book', related_name='copies')
class Loan(models.Model):
start_date = models.DateField()
end_date = models.DateField()
returned = models.BooleanField(default=False)
customer = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True,
null=True, related_name='loans')
book_copy = models.ForeignKey(
'BookCopy', on_delete=models.CASCADE, related_name='loans')
I have a fairly simple view containing a list of books
def book_list(request):
book_list = Book.objects.prefetch_related('authors', 'copies__loans')
return render(request, 'books/book_list.html', {'books': books})
To figure out if a book is available I've written the property is_available inside the Book model. However when I call this property inside my template with the following:
{% for book in books %}
{% if not book.is_available %}
-- Template stuff --
{% endif %}
{% endfor %}
According to Django Debug Toolbar I end up with a duplicate query for every Book in the queryset
Reading the Django documentation for prefetch related there's a section discribing the behaviour which I think may be the culprit
Remember that, as always with QuerySets, any subsequent chained methods which imply a different database query will ignore previously cached results, and retrieve data using a fresh database query.
How would I prevent these duplicate queries from occuring?
I have these models:
# this is model for user
class UserProfile(models.Model):
user = models.OneToOneField(User, related_name='profile')
profilepic = models.ImageField(blank=True)
city = models.ForeignKey(City)
slug = models.SlugField(unique=True)
def save(self, *args, **kwargs):
self.slug = slugify(self.user.username)
super(UserProfile, self).save(*args, **kwargs)
def __unicode__(self):
return self.user.username
#property
def avg_rating(User):
return UserProfile.userrating_set.all().aggregate(Avg('rating'))['rating__avg']
# this is the model for user ratings - one to many relationship with User
class UserRating(models.Model):
user = models.ForeignKey(User)
comment = models.CharField(max_length=500)
for_username = models.CharField(max_length=128)
rating = models.IntegerField(default=5)
def __unicode__(self):
return unicode(self.rating)
I want to refer to the result of the property (avg_rating) in my template so I can show the rating of each user. I tried this it it shows blank result:
{% for user in users %}
{{ user.profile.avg_rating }}
Also, this is the view being invoked:
def index(request):
user_list = User.objects.select_related().order_by('-userrating')[:5]
city_list = City.objects.order_by('-name')[:5]
context_dict = {"users": user_list, "cities" : city_list}
return render(request, "index.html", context_dict)
I'm currently learning my way through Django, sorry if this is very obvious.
The User argument of your avg_rating is not used at all. Try rewriting it as:
def avg_rating(self):
return self.user.userrating_set.all().aggregate(Avg('rating'))['rating__avg']
You can also remove the #property decorator as you can also use methods in Django templates using the same syntax (ie. without ()), such as:
{{ user.profile.avg_rating }}
UserRating is related to User, not UserProfile. You'd need to make the query against that model.
But note that the way you're doing it is extremely expensive, given that it requires an extra query+aggregation for every single user. Instead you should do the query in the view, via the annotation method that works on all elements in a queryset.
def index(request):
user_list = User.objects.select_related().annotate(
rating=Avg('userrating__rating')
).order_by('-rating')[:5]
Now you can refer to each user's rating via {{ user.rating }} in the template.
1)
Things like def avg_rating like to be set as Manager method. It may be better idea to create Manager class for UserProfile (UserProfileManager for example) to deal all queries of UserProfile model. The avg_rating definition would prefer to live in this UserProfileManager.
2)
The avg_rating definition is related to UserProfile so it should be call as UserProfile method not User method.
3)
Setting avg_rating in UserProfile class may be confusing while UserRating class is created for rating stuff.