Hello there I have a template that is used by two views in this template a have a toggle for deleting objects and because I have 2 types of objects that can be deleted by the same form I want to verify somehow the instance of the object in orderd to decide which url to use for deleting. How can I write this in django template properly?
<form method="POST" action="{% if object instanceof Income %}{% url 'delete-income' object.id %}{% elif object instanceof Spending %}{% url 'delete-spending' object.id %}{% endif %}">
the views extend this custom mixin to not repeat code:
class ObjectDeleteViewMixin(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
model = None
template_name = 'users/incomes_&_spendings.html'
success_url = None
def test_func(self):
self.model = self.get_object()
if self.request.user == self.model.user:
return True
return False
the views
class IncomeDeleteView(ObjectDeleteViewMixin):
model = Income
success_url = reverse_lazy('incomes')
class SpendingDeleteView(ObjectDeleteViewMixin):
model = Spending
success_url = reverse_lazy('spendings')
urls:
path('incomes/delete/<int:pk>/', IncomeDeleteView.as_view(), name='delete-income'),
path('spendings/delete/<int:pk>/', SpendingDeleteView.as_view(), name='delete-spending'),
Please don't. Django templates are deliberately restricted, not to allow function calls, subscripting, etc. to prevent people from writing business logic in the template. A template is normally focused on rendering logic. By implementing business logic in the template, you often make it templates harder to understand, update, etc.
Probably the most elegant way to solve this, is to define a method in both the Income and the Spending class that returns the path to the delete url:
from django.urls import reverse
class Income(models.Model):
# …
def remove_url(self):
return reverse('delete-income', kwargs={'pk': self.pk})
# …
class Spending(models.Model):
# …
def remove_url(self):
return reverse('delete-spending', kwargs={'pk': self.pk})
then you can render this with:
<form method="POST" action="{{ object.remove_url }}">
Related
Hellow, I am noob and I make instagram followers scraper on Django.
I make a function that should add 1 (only 1 cos it works really slow) new follower in to db which takes from already working sсraping functions and place it into class.
class ListOfFollowers(ListView):
model = Followers
context_object_name = 'followers_of'
template_name = 'insta/followers.html'
def get_follower(self, username):
loader = instaloader.Instaloader()
loader.login('***', '***')
profile = instaloader.Profile.from_username(loader.context, username)
followers = profile.get_followers()
followers_tuple = tuple(followers)
i = random.randint(0, len(followers_tuple) - 1)
login = followers_tuple[i].username
photo = followers_tuple[i].get_profile_pic_url()
url = f'https://www.instagram.com/{login}/'
mutual_subscription = followers_tuple[i] in profile.get_followees()
res = {'login': login, 'photo': photo, 'url': url, 'mutual_subscription': mutual_subscription}
return res
def add_followers(self):
username = WhoFollow.objects.get(follow_on__contains=self.kwargs['follow_id']).title
context = None
while not context:
try:
context = get_follower(username)
except:
next
context.update({'follow_on': self.kwargs['follow_id']})
res = Followers(context)
res.save()
def get_queryset(self):
return Followers.objects.filter(follow_on=self.kwargs['follow_id'])
function calls add_followers (try block is just because its not always working from 1st try)
My models
class WhoFollow(models.Model):
title = models.CharField(max_length=255)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('follow', kwargs={'follow_id': self.pk})
class Followers(models.Model):
login = models.CharField(max_length=255)
photo = models.ImageField(upload_to="photos/", blank=True)
url = models.URLField()
mutual_subscription = models.BooleanField(default=False)
time_add = models.DateTimeField(auto_now_add=True)
time_unfollow = models.DateTimeField(blank=True)
follow_on = models.ForeignKey(WhoFollow, on_delete=models.PROTECT)
def __str__(self):
return self.login
And my template
{% extends 'insta/Base.html' %}
{% block content %}
<h1>Who we find</h1>
<ul>
{% for l in object_list %}
<li>
<h5>{{l.login}}</h5>
</li>
{% endfor %}
</ul>
<!-- from there i dont understand what i should do too -->
<form action="{% url views.ListOfFollowers.}">
<button type="submit" onclick="">add another 1</button>
</form>
{% endblock %}
urls.py
urlpatterns = [
path('admin/', admin.site.urls),
path('', Home.as_view(), name='home'),
path('add/', AddWhoFollow.as_view(), name='add'),
path('follow/<int:follow_id>', ListOfFollowers.as_view(), name='follow'),]
Sorry for too many code but my head already broken on this task.
There are some issues with your code:
First, you have added the two methods get_follower() and add_followers() to the your ListView, but it is unclear how (if at all) they are called. It looks to me like you might be misunderstanding the concept of the ListView. The purpose of the ListView is to render a list of objects, not add something. So instead you want to create a new view for adding followers. You can just define a simple function view for that, does not have to be a CBV.
Second, each view needs to have it's own URL defined. You did not post your urls.py, but I suppose {% url views.ListOfFollowers.} in the template will not work. Instead, you need to define a URL name in your urls.py (one for each view) and out that in your template.
I was totaly misunderstand the concept of the CBV.
The answer is that i should do it inside CreateVeiw after form validation where i choose which login to scrap.
There is result:
class AddFollowers(CreateView):
form_class = AddFollower
model = Followers
template_name = 'insta/add_follower.html'
def get_success_url(self):
return reverse_lazy('home')
def form_valid(self, form):
# Here is a body of scraping script where i creating a list of objects to add in db
Followers.objects.bulk_create(objs=res)
return HttpResponseRedirect(f'http://127.0.0.1:8000/)
I am creating a Q&A website for practice, I created the answer and the question model and linked them together, however I can not access the template that I set for the deletion of the answer model, I created a DeleteView to delete the question. Here is the code:
views.py:
class Politics_post_details(DeleteView):
model = PoliticsPost
context_object_name = 'politicsposts'
pk_url_kwarg = 'qid'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# now you can get any additional information you want from other models
question = get_object_or_404(PoliticsPost, pk=self.kwargs.get('qid'))
context['answers'] = Answer.objects.filter(post=question).order_by('-date_posted')
return context
class AnswerDelete(UserPassesTestMixin,DetailView):
model = Answer
success_url = reverse_lazy('Lisk home')
pk_url_kwarg = 'aid'
def test_func(self):
answer = self.get_object()
if self.request.user ==answer.author:
return True
return False
urls.py(not root):
path('politicspost/<int:qid>/createanswer/',views.CreateAnswer.as_view(template_name='lisk_templates/createanswer.html'),name = 'Answer'),
path('politicspost/<int:qid>/answer/<int:aid>/delete/',views.AnswerDelete.as_view(template_name = 'lisk_templates/answer_delete.html'),name='Answer_Delete'),
path('politicspost/<int:qid>/',views.Politics_post_details.as_view(template_name='lisk_templates/politics_post_details.html'),
I created the template but whenever I try to access it, it gives me an error as follows:
NoReverseMatch at /politicspost/29/
Reverse for 'Answer_Delete' with arguments '(36,)' not found. 1 pattern(s) tried: ['politicspost/(?P<qid>[0-9]+)/answer/(?P<aid>[0-9]+)/delete/$']
Thanks in advance.
answer_delete.html:
{%extends "lisk_templates/base.html"%}
{% block title %}
Page title
{% endblock title %}
{% block body%}
<div class="feed" style="background-color:lightred;"><form method="POST">
{% csrf_token %}
<h3>Are you sure that you want to delete this answer: <br>
{{ object.content }}</h3>
<button id="signin" type="submit">Yes</button> No
</form>
</div>
{% endblock body %}
You have a number of issues. The biggest one is that you have switched DeleteView and DetailView (your DeleteView is a DetailView and vice versa).
And then you should either rename your template to answer_confirm_delete or add the template_name_suffix to your DeleteView. See the documentation for further details:
https://docs.djangoproject.com/en/3.0/ref/class-based-views/generic-editing/#django.views.generic.edit.DeleteView
If you use Django's DeleteView, you don't need to specify a url in the template, it will do all the work for you. Just make sure you specify the url correctly in your urls.py. Fix these issues and see if it works then.
May Be This Might Work For You, Because You Never Used The Import Of DeleteView
from django.views.generic import DeleteView
"""This Is From My Blog App, That I Used For My Blog App, Hope This Will Help"""
class PostDeleteView(LoginRequiredMixin, DeleteView):
model = Post
success_url = reverse_lazy('post_list')
Happy Coding, let me know if there are any issues with this I'am Up for you!
I want to render list of all objects on my template, for which their author is the currently logged in user. I passed the username of current user to url.py:
My List
My urls.py:
path('myscenarios/<str:username>/', MyScenarioListView.as_view(), name='myscenarios'),
My question is how to build the queryset in views.py and what to type in template block in my html?
class MyScenarioListView(LoginRequiredMixin, ListView):
model = Scenario
template_name = 'testmanager/myscenarios.html'
context_object_name = 'myscenarios'
def get_queryset(self):
user = get_object_or_404(User, username=self.kwargs.get('username'))
return Scenario.objects.filter(scenarioAuthor = user).order_by('-date_posted')
What code should I type in my myscenarios.html file?
I want to render list of all objects on my template, which their author is current logged user.
Then you should not encode the user in the path, since a "hacker" can then simply change the URL to see the items belonging to a different user.
You can make use of self.request.user here. The path thus looks like:
path('myscenarios/', MyScenarioListView.as_view(), name='myscenarios'),
and in the view, we use:
class MyScenarioListView(LoginRequiredMixin, ListView):
model = Scenario
template_name = 'testmanager/myscenarios.html'
context_object_name = 'myscenarios'
def get_queryset(self):
return Scenario.objects.filter(
scenarioAuthor=self.request.user
).order_by('-date_posted')
It will pass the Scenarios as myscenarios to the template, so you can render this with:
{% for scenario in myscenarios %}
{{ scenario }}
{% endfor %}
Hello I want to delete an object in a model but I don't want to show the id in the slug, so I realized I can send the data via a hidden tag in a form, but I didn't manage to make it work
Template
<form action="{% url "delete_url" %}" method="post">
{% csrf_token %}
<input type="hidden" name="pk" value={{ model.pk }}>
<button type="submit">Delete</button>
</form>
Url. Check I don't want slug
path("delete_view", views.MyDeleteView.as_view(), name="delete_url")
View
class MyDeleteView(DeleteView):
model=ModelName
success_url = reverse_lazy("success_url")
First of all, your sample code has various problems which need to be fixed:
The input type should be hidden, not hiden.
Your class based view name should have proper capitalization: MyDeleteView, not delete_view
Now, for your actual problem: The DeleteView uses SingleObjectMixin to identify the object (check this out https://ccbv.co.uk/projects/Django/2.0/django.views.generic.edit/DeleteView/). Thus, you need to override the get_object method of SingleObjectMixin so as to use the POST data instead of the slug to retrieve the object. Probably something like this should work (warning there's no error handling):
class MyDeleteView(DeleteView):
model=ModelName
success_url = reverse_lazy("success_url")
def get_object(self, queryset=None):
pk = self.request.POST['pk']
return self.get_queryset().filter(pk=pk).get()
For a gentle introduction to CBV I recommend my CBV guide: https://spapas.github.io/2018/03/19/comprehensive-django-cbv-guide/
Thanks to Serafeim I can find the way to do it, I have to replace the get_object() method and copy some code from the original method. I get the code from the link:
https://ccbv.co.uk/projects/Django/2.0/django.views.generic.edit/DeleteView/
Finaly:
class MyDeleteView(DeleteView):
model=ModelName
success_url = reverse_lazy("success_url")
def get_object(self, queryset=None):
pk = self.request.POST['pk']
if queryset is None:
queryset = self.get_queryset()
if pk is not None:
queryset = queryset.filter(pk=pk)
try:
# Get the single item from the filtered queryset
obj = queryset.get()
except queryset.model.DoesNotExist:
raise Http404("No %(verbose_name)s found matching the query" %
{'verbose_name': queryset.model._meta.verbose_name})
return obj
I'm just starting with django (and python too, to be honest)
I am trying to get a model method that would cut the self.slug from current URL and return it to template.
This is the method I tried:
class Category(models.Model):
...
def remove_filter(self):
url = HttpRequest.get_full_path()
slug = '/' + self.slug
return url.replace(slug, '')
But as you can imagine, it doesn't work.
Template's snippet:
{% for object in active_filters %}
<li><i class="icon-remove"></i>{{ object }}</li>
{% endfor %}
My core goal here is to have a front-end icon with a url altered by removing current object's slug.
I have no idea how to do it through views, but I'm open to any suggestions.
def category_page(request, url):
slugs = url.split('/')
active = Category.objects.filter(slug__in=slugs)
sorted_slugs = []
for i in active:
sorted_slugs.append(i.slug)
if slugs != sorted_slugs:
url = '/'.join(sorted_slugs)
return redirect('http://127.0.0.1:8000/catalog/' + url)
inactive = Category.objects.exclude(slug__in=slugs)
return render(request, 'category.html', {'active_filters': active,
'inactive_filters': inactive})
Thanks.
You can send a list of all active slugs to the template and then build a custom template filter to construct the modified url.
views.py
# Send your list of active slugs to the template
return render(request, 'category.html', {
'active_filters': active,
'inactive_filters': inactive,
'slugs': slugs,
})
tags_and_filters.py
import copy
from django import template
register = template.Library()
#register.filter(name='remove_filter')
def remove_filter(category, slugs):
copied_slugs = copy.copy(slugs)
slug = category.slug
if slug in copied_slugs:
copied_slugs.remove(slug)
return '/'.join(copied_slugs)
your template
{% for object in active_filters %}
<li>
<i class="icon-remove"></i>{{ object }}
</li>
{% endfor %}
Your remove_filter method has no access to the current request. HttpRequest is the class, not the current request instance.
I suggest that you rewrite remove_filter as a custom tag or filter. That way your function can access the category and request instance. You will have to activate the request template context processor in your settings as well.