Number of Post Views is not showing in template ( Function Based Views ) - python

I am Building a BlogApp and I implement a feature to count the views of Post. BUT views are not showing in post page in Browser.
What i am trying to do
I am trying to count the number of views which post will got whenever the user visits.
The Problem
Post views are not showing in post page in browser.
What have i tried
1). I also tried models.IntegerField BUT that didn't worked for me , because whenever user refresh the page then it increase one view everytime for single user.
2). I followed some tutorials BUT the all of them were on Class Based Views and I am using Function Based Views.
3). Then i thought of IP address BUT that also didn't worked for me because it wasn't working for my server.
Then i think of a ManyToManyField in views variable. BUT it also not working for me, I don't know where is the problem.
views.py
def detail_view(request,pk):
data = get_object_or_404(BlogPost,pk=pk)
queryset = BlogPost.objects.order_by('viewers')
context = {'queryset':queryset,'data':data}
return render(request, 'mains/show_more.html', context )
models.py
class BloPost(models.Model):
post_owner = models.ForeignKey(User,default='',null=True,on_delete = models.CASCADE)
date_added = models
viewers = models.ManyToManyField(settings.AUTH_USER_MODEL,related_name='viewed_posts',editable=False)
show_more.html
1). This is showing auth.User.None
{{ data.viewers }}
2). This is showing all the posts.
{{ queryset }}
I don't what am i doing wrong.
Any help would be appreciated.
Thank You in Advance.

You should annotate your queryset, so:
from django.db.models import Count
def detail_view(request,pk):
queryset = BlogPost.objects.annotate(
num_views=Count('viewers')
).order_by('-num_views')
data = get_object_or_404(queryset, pk=pk)
context = {'queryset':queryset,'data':data}
return render(request, 'mains/show_more.html', context )
and then you can render this with:
{{ data.num_views }}
If you want to add the user to the viewers, you can run the logic to add the user to the viewers:
from django.db.models import Count
def detail_view(request,pk):
queryset = BlogPost.objects.annotate(
num_views=Count('viewers')
).order_by('-num_views')
data = get_object_or_404(queryset, pk=pk)
if self.user.is_authenticated:
__, created = Post.viewers.through.objects.get_or_create(
post=data,
user=self.request.user
)
if created:
data.num_views += 1
context = {'queryset':queryset,'data':data}
return render(request, 'mains/show_more.html', context )
That being said, this to some extent demonstrates that function-based views are often not well-suited for a view that requires to run logic that consists out of a number of separate parts. In that case it is better to work with mixins and define a class-based view in terms of these mixins.

Related

Django CreateView success url with pk from different unrelated model

I have a django CreateView where users can create new words. I also have an unrelated Song model, where users can choose to add words from the lyrics of the songs. So I've created a separate CreateView for this, so that I can have a different success url. The success url should go back to the song where the user was browsing. But I am struggling to figure out how to pass the pk of this particular object to the CreateView of a different model.
This is my special CreateView:
class CreateWordFromSong(LoginRequiredMixin, generic.CreateView):
template_name = 'vocab/add_custom_initial.html'
fields = ("target_word","source_word", etc.)
model = models.Word
from videos.models import Song
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.user = self.request.user
self.object.save()
return super(CreateWordFromSong, self).form_valid(form)
success_url = reverse_lazy('videos:song-vocab', kwargs={'pk': song_pk) #how to get song_pk?
Everything works when I replace song_pk with the actual pk.
I overwrite form_valid so that the user can be saved with the new object. Perhaps there is a way I can alter this so that I can also get the song_pk? I've played around with it a bit, but without luck.
I get the song_pk from my url:
path('song/<int:song_pk>/', views.CreateWordFromSong.as_view(), name='create-song'),
So I should have access to it. But when I try adding it to my view:
class CreateWordFromSong(LoginRequiredMixin, generic.CreateView, song_pk)
I get the error: name 'song_pk' is not defined.
You could add in class CreateWordFromSong instead success_url method get_success_url
def get_success_url(self):
return reverse_lazy('videos:song-vocab', kwargs={'pk': self.kwargs.get('song_pk')})
It will be work, if action in template with form be like this
<form action="{% url 'create-song' song_pk=song.pk %}" method="post">
(Surely a bit lately ...)
I faced a similar case but not sur this matches with your.
I have a list of Mapping objects (children) related to Flow object (parent and ForeignKey).
This list uses the Flow PK.
urls.py
path('flow/<int:pk>/mapping',
mappingColumnOneFlowListView.as_view(), name='mapping-details'),
On each line of Mapping objects, I got a "delete" button.
After deletion, I want to lead back to the list of the Flow Mapping but couldn't because the PK used in the view lead to the (deleted) Mapping.
Another solution would have consisted in leading to another URL without pk (eg. absolute_url in Model) but this was not what I wanted.
It's surely not the best way to achieve the goal but I found this:
in my DeleteView i added :
def get_success_url(self):
pk = self.kwargs['pk'] # Mapping's PK
flow_pk = MappingField.objects.filter(
pk=pk).first().fl_id.pk # fl_id.pk = Flow's PK (FK)
return reverse('mapping-details', kwargs={'pk': flow_pk})

How can i save a data on database using django views?

I tried saving data on database using django views but it return a error.
def get_enroll(request, pk):
user = request.user
users = User.objects.filter(username=user)
course = Course.objects.filter(pk=pk)
chapter = ChapterModel.objects.filter(course = course)
abc = Enroll()
abc.save_enroll(users, course, chapter)
template_name = 'dashboard.html'
context = {'users':user,'course':course}
return render(request, template_name, context)
You can save it directly like:
Enroll(user=user, course=course, chapter=chapter).save()
You can simply use:
abc = Enroll.objects.create(users=users, course=course, chapter=chapter)
Since you havent provided your models, nor any logic of how you want this to work, I cannot give you a better solution than this one.
Hope this helps

Django Haystack Custom Search Form

I've got a basic django-haystack SearchForm working OK, but now I'm trying to create a custom search form that includes a couple of extra fields to filter on.
I've followed the Haystack documentation on creating custom forms and views, but when I try to view the form I can only get the error:
ValueError at /search/calibration/
The view assetregister.views.calibration_search didn't return an HttpResponse object. It returned None instead.
Shouldn't basing this on SearchForm take care of returning a HttpResponse object?
forms.py
from django import forms
from haystack.forms import SearchForm
class CalibrationSearch(SearchForm):
calibration_due_before = forms.DateField(required=False)
calibration_due_after = forms.DateField(required=False)
def search(self):
#First we need to store SearchQuerySet recieved after / from any other processing that's going on
sqs = super(CalibrationSearch, self).search()
if not self.is_valid():
return self.no_query_found()
#check to see if any date filters used, if so apply filter
if self.cleaned_data['calibration_due_before']:
sqs = sqs.filter(calibration_date_next__lte=self.cleaned_data['calibration_due_before'])
if self.cleaned_data['calibration_due_after']:
sqs = sqs.filter(calibration_date_next__gte=self.cleaned_data['calibration_due_after'])
return sqs
views.py
from .forms import CalibrationSearch
from haystack.generic_views import SearchView
from haystack.query import SearchQuerySet
def calibration_search(SearchView):
template_name = 'search/search.html'
form_class = CalibrationSearch
queryset = SearchQuerySet().filter(requires_calibration=True)
def get_queryset(self):
queryset = super(calibration_search, self).get_queryset()
return queryset
urls.py
from django.conf.urls import include, url
from . import views
urlpatterns = [
....
url(r'^search/calibration/', views.calibration_search, name='calibration_search'),
....
]
Haystack's SearchView is a class based view, you have to call .as_view() class method when adding a urls entry.
url(r'^search/calibration/', views.calibration_search.as_view(), name='calibration_search'),
This helped me.
"removing the "page" prefix on the search.html template did the trick, and was a good temporary solution. However, it became a problem when it was time to paginate the results. So after looking around, the solution was to use the "page_obj" prefix instead of "page" and everything works as expected. It seems the issue is that the haystack-tutorial assumes the page object is called "page", while certain versions of django its called "page_obj"? I'm sure there is a better answer - I'm just reporting my limited findings."
See this: Django-Haystack returns no results in search form

Django - Custom admin action

I'm creating a custom django admin action to show the selected Projects in a chart which I have in a template, the problem that I'm having is that it's displaying all the existing projects and I want just to display the ones which the user selects in the admin part.
Here's the admin.py part which should filter the projects that the user had selected:
def show_gantt_chart_of_selected_projects(modeladmin, request, queryset):
selected = request.POST.getlist(admin.ACTION_CHECKBOX_NAME)
ct = ContentType.objects.get_for_model(queryset.model)
return HttpResponseRedirect("/xxx/?ct=%s&ids=%s" % (ct.pk, ",".join(selected)))
Here's the view.py part which should get the filtered projects:
def index(request):
projects = Project.objects.order_by('projectName') // I think this line could be the problem
context = {'projects': projects }
return render_to_response('xxx/ganttChart.html', context, context_instance=RequestContext(request))
When I open the chart site, the URL show the items that the user selected correctly(ex. http://x.x.x.x:xxxx/xxx/?ct=15&ids=10,1,3,5), but the chart still show all the existing projects.
The queryset parameter to the admin action already contains the selected projects. Alter to:
def show_gantt_chart_of_selected_projects(modeladmin, request, queryset):
ct = ContentType.objects.get_for_model(queryset.model) # why do you do this, you're not using it?
return HttpResponseRedirect("/xxx/?ct=%s&ids=%s" % (ct.pk, ",".join(queryset.values_list('pk', flat=True)))
BTW you should use reverse url resolving instead of hardcoding urls.
View, which I took the liberty to switch to a class-based version. You will want to do this eventually anyway:
from django.views.generic import ListView
class IndexView(ListView):
template_name = 'xxx/ganttChart.html'
context_object_name = 'projects'
model = Project
def get_queryset(self):
return Project.objects.filter(
pk__in=self.request.GET.get('ids','').split(','),
).order_by('projectName')
index = IndexView.as_view()

merging a view with template view django

I want that the landing page of my homepage is a form with an input and the user puts in stuff. So I followed a couple of tutorials and now I have this:
views.py:
def create2(request):
if request.method =='POST':
form = LocationForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect('')
else:
form = LocationForm()
args = {}
args.update(csrf(request))
args['form'] = form
return render_to_response('location/index.html', args)
and in my urls.py:
url(r'^$', 'core.views.create2'),
which works perfectly fine, if I go to 127.0.0.1:8000 I get to index.html and when put in something in the input it gets saved in the database. However, the old part of my homepage looks like this
class LandingView(TemplateView):
model = Location
template_name="location/index.html"
def search
...
and the urls.py:
url(r'^$', core.views.LandingView.as_view(), name='index'),
which has a function search I So my question is, is there a way how I can merge the def create2 into my LandingView. I tried several things, but I am always ending up having the index.html without the input field. I also tried
def create2
...
def search
...
but didn't work.
Does anyone know how to merge that together?
EDIT
Thank you the working solution looks like this now
class Create(CreateView):
model = coremodels.Location
template_name = 'location/test.html'
fields = ['title']
def form_valid(self, form):
form.save()
return HttpResponseRedirect('')
return super(Create, self).form_valid(form)
Depending on the results you are looking for, there are multiple ways to solve this:
1. Use CreateView and UpdateView
Django already provides some classes that render a form for your model, submit it using POST, and re-render the form with errors if form validation was not successful.
Check the generic editing views documentation.
2. Override get_context_data
In LandingView, override TemplateView's get_context_data method, so that your context includes the form you are creating in create2.
3. Use FormView
If you still want to use your own defined form instead of the model form that CreateView and UpdateView generate for you, you can use FormView, which is pretty much the same as TemplateView except it also handles your form submission/errors automatically.
In any case, you can keep your search function inside the class-based view and call it from get_context_data to include its results in the template's context.

Categories

Resources