How to capture the number of downloads of a specific file - python

I have been working on a project that involves downloading files. I have been tasked to create a report of how many downloads per file. Here is my code:
reports.py
def dataset_download(request):
download = DataSet.objects.annotate(numdownload=Count('name'),)
return render(request, "my_admin/dataset_download.html", {"download":download})
my models.py
class DataSet(models.Model):
name = models.CharField(max_length=255)
description = models.TextField()
purpose = models.TextField()
temporal_scope = models.TextField()
taxonomic_scope = models.TextField()
my urls.py
path('dataset_download/', reports.dataset_download, name='dataset_download'),
and finally my html
{% for d in download %}
{% if d.name != "" %}
<tr>
<td>{{ d.name }}</td>
<td>{{ d.numdownload }}</td>
<td>
<a href="/dataset/?name={{ d.name }}" class="btn btn-primary btn-xs">
<i class="fa fa-eye"></i>
View Details
</a>
</td>
</tr>
{% endif %}
{% endfor %}

You can make use of the following Django app to keep track of all of your downloads.
You can go through the documentation here.
https://pypi.org/project/django-download-stats/

You can create a new attribute and method to your model:
class DataSet(models.Model):
name = models.CharField(max_length=255)
description = models.TextField()
purpose = models.TextField()
temporal_scope = models.TextField()
taxonomic_scope = models.TextField()
numdownload = models.IntegerField(default=0)
def increment_numdownload(self):
self.numdownload +=1
return self.numdownload
And call this method whenever download event is loaded.

Related

Django Prefetch Related Issue - Understand it correctly

I do have the following Issue - I want to display all of the bundles with their component relations in a template:
Here is my ORM-Model:
class Component(models.Model):
plenty_var_number = models.CharField(max_length=120, default=None, unique=True, null=True)
plenty_var_id = models.CharField(max_length=120, default=None, unique=True)
description = models.TextField(max_length=1000)
category = models.ForeignKey(Category, on_delete=models.DO_NOTHING, null=False)
updated_at = models.DateTimeField(auto_now=True, null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True, null=True, blank=True)
def __str__(self):
return f"{self.category.name} - {self.plenty_var_number}"
class Bundle(models.Model):
active = models.BooleanField(default=True)
plenty_var_number = models.CharField(max_length=120, default=None, unique=True, null=True)
plenty_var_id = models.CharField(max_length=120, null=True, default=None)
car = models.ForeignKey(Car, on_delete=models.DO_NOTHING)
# m2m defined by BundleComponentRelation
components = models.ManyToManyField(Component, through="BundleComponentRelation")
linked_to_plenty = models.BooleanField(default=False)
price = models.DecimalField(max_digits=10, decimal_places=2, default=-1.00)
updated_at = models.DateTimeField(auto_now=True, null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True, null=True, blank=True)
class BundleComponentRelation(models.Model):
component = models.ForeignKey(Component, on_delete=models.DO_NOTHING)
bundle = models.ForeignKey(Bundle, on_delete=models.DO_NOTHING)
qty = models.IntegerField(default=1)
updated_at = models.DateTimeField(auto_now=True, null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True, null=True, blank=True)
I have played around with select_related and prefetch_related in my view to pass them via context to the template to display it for my users:
html-template:
{% for bundle in bundles %}
<tr>
<td><p class="fw-bold">{{ bundle.plenty_var_number }}</p></td>
<td>{{ bundle.price }}</td>
<td><p class="fw-bolder mb-0">{{ bundle.car }}</p>
<p class="mb-0">{{ bundle.car.roof_type }}</p>
<p class="mb-0">BJ: {{ bundle.car.production_start }}
- {{ bundle.car.production_end }}</p>
</td>
{# Bundle Component Relations here #}
<td style="font-size: 1em;">
<a href={% url "edit_bundle" bundle.pk %}><i
class="fas fa-xl fa-edit "></i></a>
<a href={% url "sync_bundle" bundle.pk %}><i
class="fas fa-xl fa-sync "></i></a>
</td>
</tr>
{% endfor %}
views.py
def bundle_view(request):
bundles = Bundle.objects.prefetch_related('components').all()
print(bundles[0].components)
return render(request, "all_bundles.html", context={"bundles": bundles})
The output of print(bundles[0].components) is bundle.Component.None
I understood the forward usage of select_related but I do have trouble understanding the reverse thinking of the prefetch_related in my situation.
I think my problem is the lookup syntax of prefetch_related, but I might be wrong here.
What am I missing here?
Thanks in advance.
EDIT
I tried:
{% for bundle in bundles %}
<tr>
<td><p class="fw-bold">{{ bundle.plenty_var_number }}</p></td>
<td>{{ bundle.price }}</td>
<td><p class="fw-bolder mb-0">{{ bundle.car }}</p>
<p class="mb-0">{{ bundle.car.roof_type }}</p>
<p class="mb-0">BJ: {{ bundle.car.production_start }}
- {{ bundle.car.production_end }}</p>
</td>
{% for comp_rel in bundle.components.all %}
{{ comp_rel }}
{% endfor %}
<td style="font-size: 1em;">
<a href={% url "edit_bundle" bundle.pk %}><i
class="fas fa-xl fa-edit "></i></a>
<a href={% url "sync_bundle" bundle.pk %}><i
class="fas fa-xl fa-sync "></i></a>
</td>
</tr>
{% endfor %}
I wanted to get only the related components to the currently iterated bundle. The problem I get here is that the template triggers the database again:
monitoring
Simply using the bundle.component in the template led to
ManyRelatedManager object is not iterable TypError
The reason this happens is because the __str__ of the Component accesses the Category, hence for each {{ comp_rel }}, you render, it will make an extra query.
You should use a Prefetch object [Django-doc] to fetch the Categorys in the same query as the one where you fetch the Components:
from app_name.models import Bundle, Component
from django.db.models import Prefetch
def bundle_view(request):
bundles = Bundle.objects.prefetch_related(
Prefetch('components', Component.objects.select_related('category'))
)
return render(request, 'all_bundles.html', {'bundles': bundles})
There is no problem with your prefetch_related, but the way you want to access these objects, you should do bundles[0].components.all() because objects fetched with reverse relation can be accessed same way as M2M fields
Are you sure it must be prefetch_related? I think it must be select_related.
I think you should use Bundle.objects.select_related('components').all() and Component.objects.prefetch_related('bundle_set').all() with your models. But I'm not sure.
And what about template error - you shoud use {% for component in bundle.components.all %} in template.
And there same problem, mb will helpfull.

How to access two models in a single view to print their fields using single html file?

I have two models in my django application (Client and Installment). My models.py is given below:
models.py
from django.db import models
from django.utils import timezone
from django.urls import reverse
# Create your models here.
class Client(models.Model):
name = models.CharField(max_length = 100)
dob = models.SlugField(max_length = 100)
CNIC = models.SlugField(max_length = 100)
property_type = models.CharField(max_length = 100)
down_payment = models.IntegerField()
date_posted = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('client_details',kwargs={ 'pk' : self.pk})
class Installment(models.Model):
client = models.ForeignKey(Client, null=True, on_delete=models.CASCADE)
installment_month = models.CharField(max_length = 100)
installment_amount = models.IntegerField()
installment_date = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.installment_month
def get_absolute_url(self):
return reverse('installment_confirmation')
And to show the client details I have a view called 'clientDetailView' and is shown below:
view.py
class ClientDetailView(DetailView):
model = Client
model = Installment
And the template for this is called 'client_details.html' is also given below:
<!DOCTYPE html>
{% extends "property_details/base.html" %}
{% block content %}
<body>
<legend class="border-bottom mb-4"><h3>{{ object.name }}</h3></legend>
<article class="media content-section">
<div>
<p class="text-muted">Client CNIC: {{ object.CNIC }}</p>
<p class="text-muted">Date of Birth: {{ object.dob }}</p>
<p class="text-muted">Type of Property: {{ object.property_type }}</p>
<p class="text-muted">Date of Agreement: {{ object.date_posted|date:"F d, Y" }}</p>
</div>
</article>
<legend class="border-bottom mb-4"><h5>Installment History</h5></legend>
<article class="media content-section">
<div>
<!--<p class="text-muted">Installment Month: {{ object.installment_month }}</p>-->
<table id="installmentTable" style="width:100%">
<tr>
<th style="width:200px">Date</th>
<th style="width:400px">Installment Month</th>
<th style="width:100px">Amount</th>
</tr>
{% for installment in installments %}
<tr>
<td>{{ installment.installment_date }}</td>
<td>{{ installment.installment_month }}</td>
<td>{{ installment.installment_amount }}</td>
</tr>
{% endfor %}
</table>
</div>
</article>
<a class="btn btn-outline-info" type = "submit" href="{% url 'add_installment' object.id %}" >Add New Installment</a>
</body>
{% endblock %}
Up to this point, When I click on the client name, it shows all the details of the client. But I also want to see the installment details of that particlar client in the form of table on the same 'client detail' page.
For that, I made a table and tried to access the fields of installment model.
But it doesn't show anything about installments.
I have a doubt that I am accessing the fields of client model using object.name, object.dob etc. And to print installments and I am also using object.installment_month etc.
How django can differenciate between two objects?
And I also tried to import both models in 'clientDetailView' as you can see above. but it gives the following error:
Page not found (404)
Request Method: GET
Request URL: http://127.0.0.1:8000/8/
Raised by: property_details.views.ClientDetailView
No installment found matching the query

What should be the correct approach to pass in primary key into URL?

Right now I am using Class-based delete view, and my URL contains two arguments which are the primary keys of my 2 models: Post and Lesson. However, I am encountering an Attribute Error: Generic detail view LessonDeleteView must be called with either an object pk or a slug in the URLconf.
These are my two models Lesson and Post:
class Post(models.Model):
title = models.CharField(max_length=100)
image = models.ImageField(default = 'default0.jpg', upload_to='course_image/')
description = models.TextField()
price = models.DecimalField(decimal_places=2, max_digits=6)
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
rating = models.IntegerField(default = 0)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post-detail', kwargs={'pk' : self.pk})
class Lesson(models.Model):
title = models.CharField(max_length=100)
file = models.FileField(upload_to="lesson/pdf")
date_posted = models.DateTimeField(default=timezone.now)
post = models.ForeignKey(Post, on_delete=models.CASCADE, null=False, blank=False)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('lesson_upload', kwargs={'pk': self.pk})
This is my URLs.py:
path('post/<int:post_id>/lesson_uploaded/<int:lesson_id>', LessonDeleteView.as_view(), name='lesson_delete'),
Right now this is how I am trying to insert the parameters in my html template:
{% block content %}
<div id="main">
<table class="table mb-0">
<thead>
<tr>
<th>Title</th>
<th>Author</th>
<th>Download</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
{% for l in Lesson %}
<tr>
<td>
{% if l.file %}
{{ l.title }}
{% else %}
<h6>Not available</h6>
{% endif %}
</td>
<td>{{ l.post.author }}</td>
<td>{% if l.file %}
Download
{% else %}
<h6>Not available</h6>
{% endif %}
</td>
<td> <a class="btn btn-danger btn-sm mt-1 mb-1" href="{% url 'lesson_delete' post.id l.id %}">Delete</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}
This is my class-based view:
class LessonDeleteView(DeleteView):
model = Lesson
success_url = '../'
template_name = 'lesson_confirm_delete.html'
As you are deleting Lesson, you don't need to provide Post ID. You can simply use Lesson ID here. So try like this:
# url
path('post/lesson_uploaded/<int:pk>/', LessonDeleteView.as_view(), name='lesson_delete'), # using pk instead of lession_id, it will resolve the error you are facing
# template
<td> <a class="btn btn-danger btn-sm mt-1 mb-1" href="{% url 'lesson_delete' l.id %}">Delete</a>
</td>
Update
from comment section, you can override the delete method like this:
class LessonDeleteView(DeleteView):
model = Lesson
success_url = '../'
template_name = 'lesson_confirm_delete.html'
def delete(self, request, *args, **kwargs):
self.object = self.get_object()
self.object.file.delete() # <-- added file delete code
success_url = self.get_success_url()
self.object.delete()
return HttpResponseRedirect(success_url)
Why are you passing the lesson id.
Since Post gets deleted by default lesson would get deleted as well. Check this video to better understand how to pass the id and details on Delete View.
DeleteView(Class Based Views)

Django create instance from views.py not working

I have a Django app that is basically a list app for tracking jobs that the user is applying for. Users have applications and applications have questions that the user wants to ask to the interviewers should they get an interview. So I have created a many to many relationship between the models 'Application' and 'Question' called 'Ask'. Models.py:
class Application(models.Model):
status_choices = (
('NS','Not Started'),
('IP','In Progress'),
('OH','On Hold'),
('CO','Complete'),
)
app_status = models.CharField(max_length=2,choices=status_choices)
position = models.CharField(max_length=255)
company = models.ForeignKey(Company)
applied_date = models.DateField(null=True)
isDeleted = models.BooleanField(default=False, blank=True)
user = models.ForeignKey(User)
def __str__(self):
return self.position
class Questions(models.Model):
question = models.CharField(max_length=255)
category = models.CharField(max_length=25)
isDeleted = models.BooleanField(default=False, blank=True)
class Ask(models.Model): #joining table
question = models.ForeignKey(Questions)
app = models.ForeignKey(Application)
isDeleted = models.BooleanField(default=False, blank=True)
So the idea is that the user creates a list of questions and then when they get an interview, they prepare for it by looking at their list of questions and assigning them to the application through a template. Questions.html:
{% extends 'base.html' %}
{% block body %}
<h1> Questions </h1>
<div class="table-responsive" align="center">
<table class="table-hover" width="75%">
<tr>
<th> Category </th>
<th> Question </th>
<th> Create Question</th>
<form method="POST">
{% csrf_token %}
<input type="text" name="app" value="{{ request.META.HTTP_REFERER }}">
<th><input type='submit' value='Add Selected'></th>
</tr>
{% for item in query %}
<tr>
<td> {{ item.category }} </td>
<td> {{ item.question }} </td>
<td> <input type="checkbox" name="selected_q" value="{{ item.id }}"></td>
<td> Delete </td>
<td> Edit </td>
</tr>
</form>
{% endfor %}
</table>
</div>
{% endblock %}
Which is called from views.py:
class QuestionView(TemplateView):
template_name = 'question.html'
def get(self, request):
query = Questions.objects.all().order_by('category')
args = {'query': query}
return render(request, self.template_name, args)
def post(self, request):
id_list = request.POST.getlist('selected_q')
return_url = request.POST.get('app')
app = int(return_url.rsplit('/')[-1])
myApp = Application.objects.get(pk=app)
if id_list != None:
for item in id_list:
myQuestion = Questions.objects.get(pk=int(item))
Ask.objects.create(app=myApp,question=myQuestion)
return HttpResponseRedirect(return_url)
else:
query = Questions.objects.all().order_by('category')
args = {'query': query}
return render(request, self.template_name, args)
The user can only get to the questions page by going through the Application view and when that happens Questions.html captures the link that the user came from. Users then select one or more questions through a checkbox on questions.html which is captured in the post as id_list - So this should give me the two keys I need to create an instance of Ask.
However, when trying to do this it looks like everything works properly, but no instance of Ask is created and I don't get any error messages. I have tried creating a record from the python shell and it works just fine. I have also tried other methods such as using a form and instantiating Ask to call .save() afterwards, but it does not insert a record. For testing, I took out the query in views.py to check to make sure valid values are getting passed to myApp and myQuestion, so I believe that the data is all there and accurate, but it isn't creating a record for some reason.
I appreciate any help you might offer, or maybe just different ideas on how to debug this problem so I can get some error messages.
Blurb was right - wrapping the table in the form tag resolved the issue. Thanks!

access to joined models in django views and templates

for joining many models with each other, I did , for example :
Message.objects.filter(conversation__recipient__user=request.user)
when I want to use it in the template side , it doesn't show me anything. example:
{{row.conversation.recipient.user.username}}
this is my code:
model:
class Conversation(models.Model):
user = models.ForeignKey(User)
def __unicode__(self):
return self.user
class Message(models.Model):
conversation = models.ForeignKey(Conversation)
title = models.CharField(max_length=50)
body = models.CharField(max_length=500)
parent = models.IntegerField(default=0)
created = models.DateTimeField(auto_now_add=True)
def __unicode__(self):
return self.title
class Recipient(models.Model):
user = models.ForeignKey(User)
conversation = models.ForeignKey(Conversation)
is_read = models.BooleanField(default=False)
view:
def admin_index(request):
rows = Message.objects.filter(conversation__recipient__user=request.user)
return render(request,'message/admin/index.html',{'rows':rows})
template:
{% for i in rows %}
{% if not i.conversation.recipient.is_read %}
<tr class="set_bold">
{% else %}
<tr>
{% endif %}
<td>name:{{i.conversation.recipient}}</td>
<td class="col-md-0"><input type="checkbox"></td>
<td class="col-md-2">{{i.conversation.user.username}}</td>
<td>{{i.title}}</td>
<td>{{i.created|date:"y-m-d"}} <small>({{i.created|timesince}})</small></td>
</tr>
{% empty %}
<tr>
<td colspan="4">{% trans "dont have any message" %}</td>
</tr>
{% endfor %}
So how can I get access to recipient models in views and templates via Message model?
Thanks
As Recipient model has ForeignKey with Conversation model, there are many recipients for a conversation. So conversation objects will have receipient_set as queryset. You need to iterate over it and get either first/last or all objects to display.
So your template code needs to change as
{% for i in rows %}
{%for recp in i.conversation.recipient_set.all %}
{# do something with each recipient object
{% if not recp.is_read %}
<tr class="set_bold">
{% else %}
<tr>
....
{%endfor%}
{%endfor%}

Categories

Resources