I'm trying to update my an instance of Post model each time a view PostDetail is generated. So far I've tried multiple approaches but none of them worked. I know that there is ready solution (django-hitcounter) but I would like to write one myself so I can understand what is happening.
The goal there is to add 1 to post.views each time user accesses PostDetail view.
models.py
class Post(models.Model):
title = models.CharField(max_length=200, unique=True)
slug = models.SlugField(max_length=200, unique=True)
author = models.ForeignKey(
User, on_delete=models.CASCADE, related_name='blog_posts')
updated_on = models.DateTimeField(auto_now=True)
content = models.TextField()
created_on = models.DateTimeField(auto_now_add=True)
status = models.IntegerField(choices=STATUS, default=0)
views = models.IntegerField(default=0)
class Meta:
ordering = ['-created_on']
views.py
class PostDetail(generic.DetailView):
model = Post
template_name = 'blog/post_detail.html'
urls.py
urlpatterns = [
path('<slug:slug>/', views.PostDetail.as_view(), name='post_detail'),
]
Once you've got to the point where Django can return a response (eg: it's found the Post object successfully etc...) - you can increment your view count for the object then and proceed to returning the response, so if you change your view to be:
class PostDetail(generic.DetailView):
model = Post
template_name = 'blog/post_detail.html'
def render_to_response(self, context, **kwargs):
self.object.views += 1
self.object.save()
return super().render_to_response(context, **kwargs)
Related
I'm trying to do a system where an user gains points if he asks a question but the points field isn't increasing when a user does that.
my model:
class Post(models.Model):
author = models.ForeignKey(User, on_delete=models.PROTECT, related_name='post')
category = models.ForeignKey(Category, on_delete=models.PROTECT)
type = models.CharField(max_length=30, choices=TYPE, default='Question')
title = models.CharField(max_length=100, unique=True)
content = models.TextField()
views = models.IntegerField(default=0)
votes = models.ManyToManyField(User, blank=True, related_name='vote')
featured = models.BooleanField(default=False)
date_posted = models.DateTimeField(default=timezone.now)
my view:
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
success_url = '/'
fields = ['title', 'content', 'category']
def form_valid(self, form):
form.instance.author = self.request.user
form.instance.author.points + 15
return super().form_valid(form)
When I go to the current user in the admin page the value doesn't change.
You should add more clarity to your code, but as I can assume and as Thierno said you are not accessing the object and not saving it afterwards.
What you need to do is once you make your post request, --and since you need access to the user--, save your post and then do something like:
post_user = self.request.user
post_user.points +=15
post_user.save()
Django is giving me the following error when I try to access /domains/{domain}/{host}:
Generic detail view HostDetailPageView must be called with either an object pk or a slug in the URLconf.
It appears that DetailView is expecting something different than what I am providing.
urls.py
urlpatterns = [
path('domains/', DomainsPageView.as_view()),
path('domains/<str:domain>', DomainHostsPageView.as_view()),
path('domains/<str:domain>/<str:host>', HostDetailPageView.as_view()),
path('', TemplateView.as_view(template_name="hosts/index.html"))
]
views.py
class HostDetailPageView(DetailView):
template_name = 'hosts/hostdetail.html'
model = Host
# Populates list of enabled domains in the context
def get_queryset(self):
qs = super().get_queryset()
filtered = qs.filter(name=self.kwargs['host'])
if not filtered.exists():
raise Http404("Host does not exist")
# filter by a variable captured from url, for example
return filtered.first()
def get_context_data(self, **kwargs):
return super().get_context_data(**kwargs)
models.py
class Host(models.Model):
created = models.DateTimeField(auto_now_add=True)
last_updated = models.DateTimeField(auto_now=True)
name = models.CharField(unique=True, max_length=settings.MAX_CHAR_COUNT)
ip_addresses = models.ManyToManyField(IPAddress)
services = models.ManyToManyField(Service)
domain = models.ForeignKey(Domain, on_delete=models.CASCADE)
os = models.ForeignKey(OperatingSystem, on_delete=models.CASCADE, blank=True, null=True)
ciphers = models.ManyToManyField(Cipher, blank=True)
certificate = models.ForeignKey(Certificate, on_delete=models.CASCADE, blank=True, null=True)
class Meta:
ordering = ['name']
Set the pk_url_kwarg attribute and override the get_queryset(...) method of the view
class HostDetailPageView(DetailView):
template_name = 'hosts/hostdetail.html'
model = Host
pk_url_kwarg = 'name'
def get_queryset(self):
return super().get_queryset().filter(host__name=self.kwargs['host'])
I created a model on django for blog posts. Each post has two status choices: Publish or Draft. How can i change Publish to Published after the post is saved?
This is my code:
from django.db import models
from django.contrib.auth.models import User
Create your models here.
STATUS = (
(0,"Draft"),
(1,"Publish"),
)
class Post(models.Model):
title = models.CharField(max_length=200)
slug = models.SlugField(max_length=200, unique=True)
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='blog_posts')
updated_on = models.DateTimeField(auto_now=True)
content = models.TextField()
created_on = models.DateTimeField(auto_now_add=True)
status = models.Integer(choices=STATUS, default=0)
class Meta:
ordering = ['-created_on']
def __str__(self):
return self.title
from django.contrib import admin
from .models import *
Register your models here
class PostAdmin(admin.ModelAdmin):
list_display = ('title','slug','status','created_on',)
list_filter = ("status",)
search_fields = ('title', 'content')
prepopulated_fields = {'slug': ('title',)}
admin.site.register(Post,PostAdmin)
Your first way is to change your value "publish" just to "published". There's no point of having additional "publish" status. Whenever a post is saved the status field should change to "published". If anyways you need it to be there,then you can add another BooleanField like "is_published" to your model and check it in your save method so whenever the self.status was equal to "publish", make the field True. If you want to have additional checks for your model; then just write a function for your model class to change the value of "is_published".
so to change the value of "is_published" field in model;
in your Post class:
add
is_published = models.BooleanField(default=False)
then override your model save method:
def save(self, *args, **kwargs):
if self.status == 1:
self.is_published = True
super(Post, self).save(*args, **kwargs)
I have made a blogger website on Django and I would have a page where the user can see/manage their own posts, so they can edit, update and delete.
I have tried to add the page but it keeps throwing an error saying no reverse match?
I am sure how to solve this problem, is something to do with how I have added the author in the Post model to PostAuthor?
This is my models file
class PostAuthor(models.Model):
user = models.OneToOneField(User, on_delete=models.SET_NULL, null=True)
bio = models.TextField(max_length=400, help_text="Enter your bio details here.")
class Meta:
ordering = ["user", "bio"]
def get_absolute_url(self):
return reverse('post-by-author', args=[str(self.id)])
def __str__(self):
return self.user.username
class Post(models.Model):
title = models.CharField(max_length=200, unique=True)
slug = models.SlugField(max_length=200, unique=True)
author = models.ForeignKey(PostAuthor, on_delete=models.CASCADE, null=True, blank=True)
updated_on = models.DateTimeField(auto_now=True)
content = models.TextField()
created_on = models.DateTimeField(auto_now_add=True)
status = models.IntegerField(choices=STATUS, default=0)
class Meta:
ordering = ['-created_on']
def get_absolute_url(self):
return reverse('post-detail', args=[str(self.id)])
def __str__(self):
return self.title
URLs file
urlpatterns = [
path('', views.IndexPage.as_view(), name='index'),
path('posts/', views.PostList.as_view(), name='all-posts'),
path('blog/<int:pk>', views.PostDetail.as_view(), name='post-detail'),
path('blog/<int:pk>', views.PostListbyAuthorView.as_view(), name='post-by-author'),
path('accounts/', include('django.contrib.auth.urls')),
]
Views file
class PostListbyAuthorView(generic.ListView):
model = Post
template_name = 'blog/post_list_by_author.html'
def get_queryset(self):
id = self.kwargs['pk']
target_author = get_object_or_404(PostAuthor, pk=id)
return Post.objects.filter(author=target_author)
def get_context_data(self, **kwargs):
context = super(PostListbyAuthorView, self).get_context_data(**kwargs)
context['blog'] = get_object_or_404(PostAuthor, pk=self.kwargs['pk'])
return context
class IndexPage(generic.ListView):
queryset = Post.objects.filter(status=1).order_by('-created_on')
template_name = 'blog/index.html'
class PostList(generic.ListView):
queryset = Post.objects.filter(status=1).order_by('-created_on')
template_name = 'blog/all_posts.html'
class PostDetail(generic.DetailView):
model = Post
template_name = 'blog/post_detail.html'
You are not passing the user's ID on your link, try this:
{% url 'post-by-author' pk=user.id %}
I am trying to get an API endpoint api/v1/device-groups/?customer=<customer_uuid> which returns the device groups related to the customer_uuid given in the URL but am not sure how to create this.
models.py
class Customer(models.Model):
customer_uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, db_index=True)
customer_name = models.CharField(max_length=128, unique=True)
class DeviceGroup(models.Model):
group_uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, db_index=True)
customer_uuid = models.ForeignKey(Customer, on_delete=models.DO_NOTHING)
device_group_name = models.CharField(max_length=20)
color = models.CharField(max_length=8)
is_default = models.BooleanField(default=False)
serializers.py
class CustomerSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Customer
fields = ('customer_name', 'customer_uuid')
class DeviceGroupSerializer(serializers.HyperlinkedModelSerializer):
customer = CustomerSerializer(many=False, read_only=True, source='customer_uuid')
class Meta:
model = DeviceGroup
fields = ('device_group_name', 'group_uuid', 'color', 'is_default', 'customer')
I am not sure what I should do in my views.py and urls.py
urls.py
router = routers.DefaultRouter()
router.register(r'device-groups', views.DeviceGroupViewSet, base_name='device-groups')
urlpatterns = [
url(r'api/v1/', include(router.urls)),
]
My views.py that returns all device groups related to this customer_uuid upon a GET request to /api/v1/device-groups/?customer_uuid=0bc899e9-4864-4183-8bcd-06937c572143/
class DeviceGroupViewSet(viewsets.ModelViewSet):
serializer_class = DeviceGroupSerializer
queryset = DeviceGroup.objects.filter(customer_uuid='0bc899e9-4864-4183-8bcd-06937c572143')
I tried to override get_queryset like this, but it results in a KeyError
views.py
class DeviceGroupViewSet(viewsets.ModelViewSet):
serializer_class = DeviceGroupSerializer
def get_queryset(self):
return DeviceGroup.objects.filter(customer_uuid=self.kwargs['customer_uuid'])
What do I need to change to get an API endpoint /api/v1/device-groups/?customer=<customer_uuid>/ that returns filtered device groups?
EDIT
Changing my views.py solved it for me.
class DeviceGroupViewSet(viewsets.ModelViewSet):
serializer_class = DeviceGroupSerializer
def get_queryset(self):
return DeviceGroup.objects.filter(customer_uuid=self.request.GET['customer_uuid'])
Anything after the ? in a URL is considered to be a list of query parameters: ?customer=<uuid> means you're passing the query parameter customer to your request. They are not part of the actual URL path.
These query parameters are all added to the QueryDict request.GET by Django. In DRF, they can be accessed in request.data as well.