Class generic DeleteView not working for model related to model - django

I have a page containing assignment questions.
Questions related to assignment is displayed on asisgnment details page with EDIT and DELETE anchor tags.
But after pressing delete I get an error : Reverse for 'CreateQuestion' with arguments '('',)' not found. 1 pattern(s) tried: ['assignment/(?P[0-9]+)/createQuestion/$']
views.py
class AssignmentDelete(DeleteView):
model = Assignment
template_name = "dashboard/assignment_list.html"
success_url = reverse_lazy('ViewAssignment')
def get(self, request, *args, **kwargs):
return self.delete(request, *args, **kwargs)
class AssignmentDetailView(generic.DetailView):
model = Assignment
template_name = "dashboard/assignment_detail.html"
class QuestionDeleteView(DeleteView):
model = Question
template_name = 'dashboard/assignment_detail.html'
def get_success_url(self):
return reverse_lazy('assignment_detail', kwargs={'pk': self.object.assignment_id})
urls.py
path('<int:assignment_pk>/DeleteQuestion/<int:pk>/delete', views.QuestionDeleteView.as_view(), name='DeleteQuestion'),
path('<int:pk>/createQuestion/', views.QuestionCreate, name='CreateQuestion'),
path('assignment/<int:pk>', views.AssignmentDetailView.as_view(), name='assignment_detail'),
assignment_detail.html
{% extends "./base.html" %}
{% block content %}
<h1>Course:{{assignment.course }}</h1>
<p><strong>Assignment:</strong> {{ assignment.number }}</p>
<p><strong>publish_date:</strong> {{ assignment.publish_date }}</p>
<p><strong>deadline:</strong> {{assignment.deadline_date}}</p>
<div style="margin-left:20px;margin-top:20px">
<h4>Questions</h4>
<table class="table">
<thead class="thead-light">
<tr>
<th scope="col">Question Title</th>
<th scope="col">Question Marks</th>
<th scope="col">Edit Question</th>
<th scope="col">Delete Question</th>
</tr>
</thead>
<tbody>
{% for question in assignment.question_set.all %}
<tr>
<td>{{ question.title }}</td>
<td>{{ question.marks }}</td>
<td> Edit </td>
<td> <a onclick="return confirm('Are you sure?');" href="{% url 'DeleteQuestion' assignment_pk=assignment.id pk=question.id %}">Delete</a></td>
</tr>
{% endfor %}
</tbody>
</table>
<a class=" btn btn-primary" href="{% url 'CreateQuestion' assignment.id %}">Add Question</a>
</div>
{% endblock %}
{% block script %}
{% endblock %}
Models.py
class Assignment(models.Model):
number = models.IntegerField()
course = models.ForeignKey(Course,on_delete = models.SET_NULL,blank=True,null=True)
publish_date = models.DateField(auto_now_add = True)
deadline_date = models.DateField()
faculty = models.ForeignKey(Faculty,on_delete = models.SET_NULL,blank=True,null=True)
def __str__(self):
return f'Assignment {self.number}-{self.course}'
def get_absolute_url(self):
"""Returns the url to access a detail record for this Assignment."""
return reverse('assignment-detail', args=[str(self.id)])
class Question(models.Model):
assignment = models.ForeignKey(Assignment, on_delete=models.SET_NULL,blank=True,null=True)
username = models.ForeignKey(User,on_delete=models.SET_NULL,blank=True,null=True)
title = models.CharField(max_length = 200)
description = models.TextField(blank=True , null = True)
marks = models.IntegerField(null=True)
date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
def get_absolute_url(self):
"""Returns the url to access a detail record for this book."""
return reverse('question-detail', args=[str(self.id)])
The Assignment detail page with questions associated with it

I did some changes to my code and added a get method and now its working as I wanted it to.
It would be great if someone point out any risk related it.Thank you all for your time in helping in my problem
here is the UPDATED code :
class QuestionDeleteView(DeleteView):
model = Question
template_name = "dashboard/assignment_detail.html"
self.object.assignment_id})
def get(self, request, *args, **kwargs):
return self.delete(request, *args, **kwargs)
def get_success_url(self):
return reverse_lazy('assignment_detail', kwargs={'pk': self.object.assignment_id})

Related

How to check if the object is assigned to another object | Django

I am working on a django case like below:
models.py
class Room(models.Model):
name = models.CharField("Room No.",max_length=200)
class Meta:
verbose_name_plural = "Room"
def __str__(self):
return self.name
class Student(models.Model):
name = models.CharField("name",max_length=200)
father_name = models.CharField("father Name",max_length=200)
cell_no = models.CharField("cell No",max_length=200)
address = models.CharField("address",max_length=500)
room = models.ForeignKey(Room, on_delete=models.CASCADE, null=True, blank=True, related_name='all_rooms')
class Meta:
verbose_name_plural = "Student"
def __str__(self):
return self.name
views.py
def room(request):
allrooms= Room.objects.all()
form = RoomForm(request.POST or None, request.FILES or None)
if form.is_valid():
form.save()
messages.success(request, "Room added successfully.")
return redirect('/room')
context = {'allrooms':allrooms, 'form':form}
return render(request, 'room.html', context)
In templates in room.html I want to show the status Vacant/Occupied based on the fact if a room is assigned to some student or not. I have the following code in template but it shows 'Vacant' status for all rooms.
<table id="example1" class="table table-bordered table-striped">
<thead>
<tr>
<th>Room</th>
<th class="text-center">Status</th>
<th class="text-center">Action</th>
</tr>
</thead>
<tbody>
{% for room in allrooms %}
<tr>
<td>{{ room.name }}</td>
<td class="text-center">
{% if room.student_set.all %}
<small class="badge badge-danger">Occupied</small>
{% elif not room.student.all %}
<small class="badge badge-success">Vacant</small>
{% endif %}
</td>
<td class="text-center"><i class="fas fa-edit"></i></td>
</tr>
{% endfor %}
</tbody>
</table>
Please help someone to show he status of the room.
to get assigned and unassigned rooms you have to write queries with respect to the related field(in this case the foreign key "all_rooms") as follows:
total_rooms = Room.objects.all().annotate(num_rooms=Count("all_rooms"))
assigned_rooms = total_rooms.filter(num_rooms__gt=0)
unassigned_rooms = total_rooms.exclude(num_rooms__gt=0)
On running, these queries will return the room instances:

Django-Filter Package: How To Filter Objects To Create Stats And Not Lists

I'm using Django-Filter package which is working well for the one example they have in their documentation. However, I am not trying to generate a list of objects rather filter the calculations I have done on the object fields.
Example template: https://django-filter.readthedocs.io/en/stable/guide/usage.html#the-template
{% for obj in filter.qs %}
{{ obj.name }} - ${{ obj.price }}<br />
{% endfor %}
The problem here is that a list is created.. I'm looking for my trade "stats" to be updated based on the new filter selections.
I believe I need to set up my views differently and possibly call the template objects in another way as well but not totally sure.
filters.py
class StatsFilter(django_filters.FilterSet):
class Meta:
model = Trade
fields = ['type', 'asset', 'symbol', 'broker', 'patterns', 'associated_portfolios']
views.py
class StatsView(LoginRequiredMixin, FilterView):
model = Trade
template_name = 'dashboard/stats.html'
filterset_class = StatsFilter
def get_form(self, *args, **kwargs):
form = StatsFilter()
user = self.request.user
form.fields['associated_portfolios'].queryset = Portfolio.objects.filter(user=user)
return form
def get_context_data(self, *args, **kwargs):
trade = Trade.objects.filter(user=self.request.user, status='cl').order_by('created')
all_trades = Trade.objects.filter(user=self.request.user, status='cl').count()
context = super(StatsView, self).get_context_data(*args, **kwargs)
data = [t.profit_loss_value_fees for t in trade]
context['all_trades'] = all_trades
context['gross_profit'] = sum([t.profit_loss_value for t in trade])
context['net_profit'] = sum([t.profit_loss_value_fees for t in trade])
...
return context
stats.html
<form method="get" class="row">
{{ filter.form.as_p }}
{{ form.media }}
<div class="col-xl-12">
<button class="btn btn-success float-right" type="submit">Apply</button>
</div>
</form>
<table class="table table-striped">
<tr>
<th scope="row">Net Profit <small>(Fees Included)</small></th>
<td>
{% if net_profit >= 0 %}
<font color="green">{{ net_profit|floatformat:2 }}</font>
{% else %}
<font color="red">{{ net_profit|floatformat:2 }}</font>
{% endif %}
</td>
</tr>
<tr>
<th scope="row">Gross Profit</th>
<td>{{ gross_profit|floatformat:2 }}</td>
</tr>
The secret sauce that took me weeks to find out is so obvious. Run the calcs over the filter. Example below. Also you do not need to use aggregate. Works just as well with the original way I wrote above.
context['gross_profit'] = sum([t.profit_loss_value for t in trade])
This is the key part:
trades = filter.qs.filter(status='cl')
view.py
def get_context_data(self, *args, **kwargs):
filter = StatsFilter(self.request.GET, queryset=self.get_queryset(), request=self.request)
trades_count = filter.qs.filter(status='cl').count()
trades = filter.qs.filter(status='cl')
...
context['gross_profit'] = trades.aggregate(value=Sum('profit_loss_value'))['value']

How to create a dynamic filter based on the primary key of ForeignKey in ListView?

I am relatively new to Django but the main problem I am facing right now is to create a ListView that will display uploaded documents based on the primary key of my ForeignKey.
I have tried several methods of trying to create the filter and read the online documentation on class-based view but it does not seem to have relevant information on how to use the primary key of my ForeignKey in my filter.
These are my models:
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)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('lesson_upload', kwargs={'pk': self.pk})
Here is my ListView with the filter that is not working:
class LessonListView(ListView):
model = Lesson
template_name = 'store/uploaded_lesson.html'
context_object_name = 'lesson'
# def get_queryset(self):
# return Lesson.objects.filter(Post__pk=self.Post.pk)
def get_queryset(self):
self.post__pk = get_object_or_404(post__pk,
name=self.kwargs['post__pk'])
return Lesson.objects.filter(post__pk=self.post__pk)
Here is my urls.py:
path('post/<int:pk>/lesson_uploaded/', LessonListView.as_view(), name='lesson_uploaded'),
Here is my html:
{% extends "store/base.html" %}
{% 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 lesson in lesson %}
<tr>
<td>
{% if lesson.file %}
<img src="{{ lesson.file.url }}" style="width:100px;">
{% else %}
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}
You can try like this:
In urls, add post_id :
path('lessons/<int:post_id>/', LessonListView.as_view()),
Then update the View to get the post_id in get_queryset method:
class LessonListView(ListView):
model = Lesson
template_name = 'store/uploaded_lesson.html'
context_object_name = 'lesson'
def get_queryset(self):
return Lesson.objects.filter(post_id=self.kwargs.get('post_id'))
Also, please don't name list and item of that list in a for loop same, so update it to:
{% for l in lesson %}. // or anything other than lesson
<tr>
<td>
{% if l.file %}

Django class based view to query database from form and display results

So I am completely new to Django, I want to have a user enter a keyword into an HTML form then have each row from the database where an attribute matches that keyword displayed on the page. I've tried various ways of doing this and am not sure what I am doing wrong. Any help would be appreciated.
search.html
<div class="container">
<form method="GET" action="{% url 'search' %}">
<div class="form-group">
<input type="text" name="make" placeholder="Car Make" />
<label>
<button type="submit" class="btn btn-danger"> Go </button>
</label>
</div>
</form>
{% if results %}
<table>
<tr>
<th scope="col"></th>
<th scope="col">Car Make</th>
<th scope="col">Car Model</th>
<th scope="col">Car Type</th>
<th scope="col">Number of Seats</th>
<th scope="col">Price</th>
</tr>
{% for item in results%}
<tr>
<td>{{item.makename}}</td>
<td>{{item.model}}</td>
<td>{{item.seriesname}}</td>
<td>{{item.seatingcapacity}}</td>
<td>{{item.pricenew}}</td>
</tr>
{% endfor %}
</table>
{% endif %}
</div>
views.py
class SearchView(TemplateView):
template_name = 'carproject/search.html'
model = Vehicles
def get(self, request):
form = AdvancedSearch()
return render(request, self.template_name, {'form': form})
def search(self, request):
makequery = self.request.GET.get['make']
if makequery:
results = self.Vehicles.objects.filter(makename__icontains(makequery))
return render(request, self.template_name, {'results': results})
Models.py
class Vehicles(models.Model):
carid = models.IntegerField(db_column='CarID', primary_key=True)
makename = models.CharField(db_column='MakeName', max_length=45)
model = models.CharField(db_column='Model', max_length=45)
seriesname = models.CharField(db_column='SeriesName', max_length=45)
seriesyear = models.TextField(db_column='SeriesYear')
pricenew = models.IntegerField(db_column='PriceNew')
fuelsystem = models.CharField(db_column='FuelSystem', max_length=45)
enginesize = models.CharField(db_column='EngineSize', max_length=10)
tankcapacity = models.CharField(db_column='TankCapacity', max_length=10)
power = models.CharField(db_column='Power', max_length=10)
seatingcapacity = models.IntegerField(db_column='SeatingCapacity')
standardtransmission = models.CharField(db_column='StandardTransmission', max_length=45)
bodytype = models.CharField(db_column='BodyType', max_length=45)
drive = models.CharField(db_column='Drive', max_length=3)
wheelbase = models.CharField(db_column='WheelBase', max_length=10)
class Meta:
managed = False
db_table = 'vehicles'
You can just do Vehicles.objects.filter(makename__icontains=request.GET.get("make","somevalueasdefault")) in your get function. Maybe I am missing something, but I am not sure why you have rendered the view like that in a class-based view. Just as an example, you can do like below.
class SearchView(TemplateView):
template_name = "carproject/search.html"
def get(self, kwargs):
context = super(SearchView, self).get_context_data(**kwargs)
context['queryset'] = Vehicles.objects.filter(makename__icontains=request.GET.get("make","sdefault"))
return context

Django 2.0.5 display different queryset result with the CBV listview

Hi: I am trying to build a staff directory where user can filter view by department. I have the department list displayed in a dropdown button but cannot figure out how to properly pass the user selection to the listview in the views.py Please see the code below.
models.py
class Department(models.Model):
department = models.CharField(max_length = 20, unique = True)
def __str__(self):
return self.department
class EmployeeList(models.Model):
department = models.ForeignKey(Department, on_delete = models.SET_NULL, null = True, blank = True)
views.py
class EmployeeOutput(ListView):
model = models.EmployeeList
context_object_name = 'employee_list'
template_name = 'employee_list.html'
def get_context_data(self, **kwargs):
context = super(EmployeeOutput, self).get_context_data(**kwargs)
context['filter_list'] = models.Department.objects.values_list('department', flat = True)
return context
class FilterDepartment(ListView):
model = models.EmployeeList
context_object_name = 'employee_list'
template_name = 'employee_list.html'
def get_context_data(self, **kwargs):
context = super(FilterDepartment, self).get_context_data(**kwargs)
context['filter_list'] = models.Department.objects.values_list('department', flat = True)
context['department_filter'] = models.EmployeeList.objects.filter(department_id = 3)
return context
employee_list.html
<table class = 'table table-striped table-hover' id = 'my_table'>
<thead>
<tr>
<th><button type="button" class = ' btn btn-info' onclick = 'sortTable(0)'>Name</button></th>
<th><button type="button" class = ' btn btn-info' onclick = 'sortTableNumbers(1)'>Phone Ex</button></th>
<th><button type="button" class = ' btn btn-info' onclick = 'sortTable(2)'>Email</button></th>
<th>
<div class="btn-group dropright">
<button class="btn btn-success dropdown-toggle" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Department
</button>
<div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
<a class="dropdown-item" onclick = 'sortTable(3)'><b>Sort Department</b></a>
<h6 class="dropdown-header">Filter by:</h6>
<div class="dropdown-divider"></div>
{% for running in filter_list%}
<a class="dropdown-item" href="{% url 'employee:department_filter' %}"><b>{{running}}</b></a>
{% endfor %}
</div>
</div>
</th>
<th><button type="button" class = ' btn btn-info' onclick = 'sortTable(4)'>Remote Access</button></th>
<th>Cell Phone</th>
</tr>
</thead>
<tbody>
{% if department_filter %}
{% for EL in department_filter %}
<tr>
<td>{{EL.first_name}} {{EL.last_name}}</td>
<td>{{EL.phone_ex}}</td>
<td> {{EL.email}}</td>
<td>{{EL.department}}</td>
<td>{{EL.remote_access}}</td>
<td>{{EL.cell}}</td>
</tr>
{% endfor %}
{% else %}
{% for EL in employee_list %}
<tr>
<td>{{EL.first_name}} {{EL.last_name}}</td>
<td>{{EL.phone_ex}}</td>
<td> {{EL.email}}</td>
<td>{{EL.department}}</td>
<td>{{EL.remote_access}}</td>
<td>{{EL.cell}}</td>
</tr>
{% endfor %}
{% endif %}
</tbody>
</table>
I can properly display the entire staff with 'employee_list'. I can show a list of current department in the dropdown button with 'filter_list'. When user clicked on any of the selection, it always shows the same result since 'department_list' is hard coded to department_id = 3. What I need to find out is how to pass in the ID with href as
{% url 'employee:department_filter' dept=running.id %}
with url.py
path('filter/<int:dept>', views.FilterDepartment.as_view(), name = 'department_filter'),
I am struggling to find out how to pass 'dept' into the 'FilterDepartment' view. Maybe there is a better way to do this or I am just missing the last piece to make this happen. Any suggestion is welcome. Thank you
suggestion by #dirkgroten
employee_list.html
<a class="dropdown-item" href="{% url 'employee:department_filter' dept=running.id %}"><b>{{running}}</b></a>
{% if object_list %}
{% for EL in object_list %}
<tr>
<td>{{EL.first_name}} {{EL.last_name}}</td>
<td>{{EL.phone_ex}}</td>
<td> {{EL.email}}</td>
<td>{{EL.department}}</td>
<td>{{EL.remote_access}}</td>
<td>{{EL.cell}}</td>
</tr>
{% endfor %}
urls.py
path('filter/<int:dept>/', views.FilterDepartment.as_view(), name = 'department_filter'),
views.py
class FilterDepartment(ListView):
model = models.EmployeeList
context_object_name = 'employee_list'
template_name = 'employee_list.html'
def get_context_data(self, **kwargs):
context = super(FilterDepartment, self).get_context_data(**kwargs)
context['filter_list'] = models.Department.objects.values_list('department', flat = True)
return context
def get_queryset(self, **kwargs):
return super().get_queryset().filter(department_id=self.kwargs['dept'])
working solution:
employee_list.html
{% for running in filter_list%}
<a class="dropdown-item" href="{% url 'employee:department_filter' running %}"><b>{{running}}</b></a>
{% endfor %}
{% if object_list %}
{% for EL in object_list %}
<tr>
<td>{{EL.first_name}} {{EL.last_name}}</td>
<td>{{EL.phone_ex}}</td>
<td> {{EL.email}}</td>
<td>{{EL.department}}</td>
<td>{{EL.remote_access}}</td>
<td>{{EL.cell}}</td>
</tr>
urls.py
path('filter/<department>/', views.FilterDepartment.as_view(), name = 'department_filter'),
views.py
class FilterDepartment(ListView):
model = models.EmployeeList
context_object_name = 'employee_list'
template_name = 'employee_list.html'
#if missing, it is looking for EmployeeList_list.html
#employee_list.pk used in empllyee_list.html
def get_context_data(self, **kwargs):
context = super(FilterDepartment, self).get_context_data(**kwargs)
context['filter_list'] = models.Department.objects.values_list('department', flat = True)
# context['department_filter'] = models.EmployeeList.objects.filter(department_id = self.kwargs['dept'])
return context
def get_queryset(self):
return super(FilterDepartment, self).get_queryset().filter(department__department=self.kwargs['department'])
Use the ListView's get_queryset() method to filter the list of objects you want to display. This will add object_list to the context in the template. Only use get_context_data() to add additional information to the context.
class FilterDepartment(ListView):
# properties like model and template_name
# context_object_name can be used if you want to call the list something else than 'object_list' in your context
def get_queryset(self):
return super().get_queryset().filter(department_id=self.kwargs['dept'])
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) # this will already include object_list
context['filter_list'] = models.Department.objects.values_list('department', flat = True)
return context
The Django documentation on ListView is quite concise and makes it difficult to really understand which methods to override. A better place to understand Django's generic class based views is here.