I have such a templatetag:
def link(obj):
return reverse('admin:%s_%s_change' % (obj._meta.app_label, obj._meta.module_name), args=[obj.id])
class AdminEditNode(template.Node):
def __init__(self, object):
self.object = template.Variable(object)
def render(self, context):
return link(self.object.resolve(context))
def edit_link(parser, token):
try:
#split content
tag_name, info = token.split_contents()
except ValueError:
raise template.TemplateSyntaxError(
'%r tag requires one model argument' % token.contents.split()[0])
return AdminEditNode(info)
register.tag('edit_link', edit_link)
It renders a link to a admin edit page of the object that is in the context of the template that I send there in my view:
def home(request):
"""
Home page view
"""
context = Contact.objects.first()
return render(request, 'home.html', {'info': context})
I need to make test that there won`t be errors if context would be a string or integer or None. My question how to make "if" where I can prevent this errors ?
You'll probably want to use isinstance. So maybe something like this:
class AdminEditNode(template.Node):
def __init__(self, object):
self.object = template.Variable(object)
def render(self, context):
resolved = self.object.resolve(context)
if not isinstance(resolved, models.Model):
# Maybe you want to raise an exception here instead?
return ''
return link(resolved)
Related
What my view does, is takes an even from id, and returns an html page containing a table of all the details.
It is a simple enough view:
class Edetails(View, SuperuserRequiredMixin):
template = 'events/details.html'
def get(request, self, pk):
event = Event.objects.get(id=pk)
count = event.participants.count()
participants = event.participants.all()
ctx = {
'count': count,
'participants': participants,
'pk': pk
}
return render(request, self.template, ctx)
This is the SuperUserRequiredMixin:
class SuperuserRequiredMixin(AccessMixin):
"""
Mixin allows you to require a user with `is_superuser` set to True.
"""
def dispatch(self, request, *args, **kwargs):
if not request.user.is_superuser:
return self.handle_no_permission(request)
return super(SuperuserRequiredMixin, self).dispatch(
request, *args, **kwargs)
And the Access Mixin:
class AccessMixin(object):
"""
'Abstract' mixin that gives access mixins the same customizable
functionality.
"""
login_url = None
raise_exception = False
redirect_field_name = REDIRECT_FIELD_NAME # Set by django.contrib.auth
redirect_unauthenticated_users = False
def get_login_url(self):
"""
Override this method to customize the login_url.
"""
login_url = self.login_url or settings.LOGIN_URL
if not login_url:
raise ImproperlyConfigured(
'Define {0}.login_url or settings.LOGIN_URL or override '
'{0}.get_login_url().'.format(self.__class__.__name__))
return force_string(login_url)
def get_redirect_field_name(self):
"""
Override this method to customize the redirect_field_name.
"""
if self.redirect_field_name is None:
raise ImproperlyConfigured(
'{0} is missing the '
'redirect_field_name. Define {0}.redirect_field_name or '
'override {0}.get_redirect_field_name().'.format(
self.__class__.__name__))
return self.redirect_field_name
def handle_no_permission(self, request):
if self.raise_exception:
if (self.redirect_unauthenticated_users
and not request.user.is_authenticated):
return self.no_permissions_fail(request)
else:
if (inspect.isclass(self.raise_exception)
and issubclass(self.raise_exception, Exception)):
raise self.raise_exception
if callable(self.raise_exception):
ret = self.raise_exception(request)
if isinstance(ret, (HttpResponse, StreamingHttpResponse)):
return ret
raise PermissionDenied
return self.no_permissions_fail(request)
def no_permissions_fail(self, request=None):
"""
Called when the user has no permissions and no exception was raised.
This should only return a valid HTTP response.
By default we redirect to login.
"""
return redirect_to_login(request.get_full_path(),
self.get_login_url(),
self.get_redirect_field_name())
However, when i click on the link to send a get request to the view:
View Participants
I get this error message:
AttributeError at /Edetails2
'WSGIRequest' object has no attribute 'template'
I don't understand what this means. How can i fix this?
i am using django version 3 and when i tried to work on model managers i got this error
ModelManager is giving error
models.py
class ProductManager(models.Manager):
def get_queryset(self):
return ProductQuerySet(self.model, using=self._db)
def features(self):
return self.get_queryset().featured()
def get_by_id(self, id):
qs = self.get_queryset().filter(id=id)
if qs.count() == 1:
return qs.first()
return None
views.py
class ProductDetailView(DetailView):
template_name = "Products/details.html"
def get_context_data(self, *args, **kwargs):
context = super(ProductDetailView, self).get_context_data(*args, **kwargs)
print(context)
return context
def get_object(self, *args, **kwargs):
request = self.request
pk = self.kwargs.get('pk')
instance = Product.objects.get_by_id
if instance is None:
raise Http404('Product does not exist')
return instance
error:
AttributeError: 'Manager' object has no attribute 'get_by_id'
Well, the problem is that Django model object querysets don't have a method named 'get_by_id'. You should just use what's provided with Django. This is likely to be what you want:
Product.objects.get(id=1)
Substitute 1 by the id that you want..
I have the following ListView. I know about get_object_or_404. But is there a way to show a 404 page if the object doesn't exist?
class OrderListView(ListView):
template_name = 'orders/order_list.html'
def get_queryset(self):
return OrderItem.objects.filter(
order__order_reference=self.kwargs['order_reference'],
)
You can raise a 404 error for a ListView, by changing the allow_empty [django-doc] attribute to False:
class OrderListView(ListView):
template_name = 'orders/order_list.html'
allow_empty = False
def get_queryset(self):
return OrderItem.objects.filter(
order__order_reference=self.kwargs['order_reference'],
)
If we inspect the soure code of the BaseListView (a class that is one of the ancestors of the ListView class), then we see:
class BaseListView(MultipleObjectMixin, View):
"""A base view for displaying a list of objects."""
def get(self, request, *args, **kwargs):
self.object_list = self.get_queryset()
allow_empty = self.get_allow_empty()
if not allow_empty:
# When pagination is enabled and object_list is a queryset,
# it's better to do a cheap query than to load the unpaginated
# queryset in memory.
if self.get_paginate_by(self.object_list) is not None and hasattr(self.object_list, 'exists'):
is_empty = not self.object_list.exists()
else:
is_empty = not self.object_list
if is_empty:
raise Http404(_("Empty list and '%(class_name)s.allow_empty' is False.") % {
'class_name': self.__class__.__name__,
})
context = self.get_context_data()
return self.render_to_response(context)
So it also takes pagination, etc. into account, and shifts the responsibility at the get(..) function level.
You can use get_list_or_404:
from django.shortcuts import get_list_or_404
def get_queryset(self):
my_objects = get_list_or_404(OrderItem, order__order_reference=self.kwargs['order_reference'])
I am writing a mixin that would enable for partial saving of form. For more details on this, please see my previous post.. while that is fixed, I have another error. I think the error comes from post method of mixing for not returnig HttpResponse Object. I am doing a super call which should take care of that. But that didn't work.
Error:
Exception Type: ValueError
Exception Value: The view blog.views.ArticleCreateView didn't return an HttpResponse object.
Here is the mixin:
class PendFormMixin(object):
form_hash_name = 'form_hash'
pend_button_name = 'pend'
def get_form_kwargs(self):
"""
Returns a dictionary of arguments to pass into the form instantiation.
If resuming a pended form, this will retrieve data from the database.
"""
form_hash = self.kwargs.get(self.form_hash_name)
print "form_hash", form_hash
if form_hash:
import_path = self.get_import_path(self.get_form_class())
return {'data': self.get_pended_data(import_path, form_hash)}
else:
print "called"
# print super(PendFormMixin, self).get_form_kwargs()
return super(PendFormMixin, self).get_form_kwargs()
def post(self, request, *args, **kwargs):
"""
Handles POST requests with form data. If the form was pended, it doesn't follow
the normal flow, but saves the values for later instead.
"""
self.object = None
if self.pend_button_name in self.request.POST:
print "here"
form_class = self.get_form_class()
print form_class
form = self.get_form(form_class)
# print "form is ", form
self.form_pended(form)
super(PendFormMixin, self).post(request, *args, **kwargs)
else:
super(PendFormMixin, self).post(request, *args, **kwargs)
# Custom methods follow
def get_import_path(self, form_class):
return '{0}.{1}'.format(form_class.__module__, form_class.__name__)
def get_form_hash(self, form):
content = ','.join('{0}:{1}'.format(n, form.data[n]) for n in form.fields.keys())
return md5(content).hexdigest()
def form_pended(self, form):
import_path = self.get_import_path(self.get_form_class())
form_hash = self.get_form_hash(form)
print "in form_pended"
pended_form = PendedForm.objects.get_or_create(form_class=import_path,
hash=form_hash)
print "form_class", import_path
print "form_hash", form_hash
print "pended_form", pended_form
for name in form.fields.keys():
pended_form[0].data.get_or_create(name=name, value=form.data[name])
print pended_form[0]
return form_hash
def get_pended_data(self, import_path, form_hash):
data = PendedValue.objects.filter(import_path=import_path, form_hash=form_hash)
return dict((d.name, d.value) for d in data)
Here is the View:
class ArticleCreateView(PendFormMixin, CreateView):
form_class = ArticleForm
model = Article
template_name = "article_create.html"
success_url = '/admin'
def form_valid(self, form):
"""
If the request is ajax, save the form and return a json response.
Otherwise return super as expected.
"""
if self.request.is_ajax():
self.object = form.save()
time.sleep(5)
return HttpResponse(json.dumps("success"),
mimetype="application/json")
return super(ArticleCreateView, self).form_valid(form)
def form_invalid(self, form):
"""
We haz errors in the form. If ajax, return them as json.
Otherwise, proceed as normal.
"""
if self.request.is_ajax():
return HttpResponseBadRequest(json.dumps(form.errors),
mimetype="application/json")
return super(ArticleCreateView, self).form_invalid(form)
The post method of a Class-Based View should return a HttpResponse object (as the exception says, obviously). In your mixin, you forgot to return something, hence the error. You just need to return the result from CreateView, from your super() call:
def post(self, request, *args, **kwargs):
"""
Handles POST requests with form data. If the form was pended, it doesn't follow
the normal flow, but saves the values for later instead.
"""
self.object = None
if self.pend_button_name in self.request.POST:
# You do things there
# It is the same line, you don't need an else block then. Note the "return"
return super(PendFormMixin, self).post(request, *args, **kwargs)
Calling super() does not make your method return anything, it only calls the superclass. You should use return super(...) in your PendFormMixin.post() method.
I have to specify a success_url, otherwise I get an error. So how to specify it, in order to stay to the same page?
Also, is everything else correct regarding the SearchView, beucase I have a feeling that something is missing. My context should be composed by form, query, concepts, language and languages.
Thanks
urls.py
url(r'^(?P<langcode>[a-zA-Z-]+)/search/$', SearchView.as_view(), name='search').
views.py
class _LanguageMixin(object):
def dispatch(self, request, *args, **kwargs):
self.langcode = kwargs.pop("langcode")
self.language = get_object_or_404(Language, pk=self.langcode)
return super(_LanguageMixin, self).dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super(_LanguageMixin, self).get_context_data(**kwargs)
context.update({"language": self.language,
"languages": Language.objects.values_list('code',
flat=True)})
return context
class SearchView(_LanguageMixin, FormView):
template_name = "search.html"
form_class = SearchForm
success_url = #......
query = ''
concepts = []
def get_initial(self):
return {'langcode': self.langcode}
def get_context_data(self, **kwargs):
context = super(SearchView, self).get_context_data(**kwargs)
context.update({"query": self.query, "concepts": self.concepts})
return context
def form_valid(self, form):
self.query = form.cleaned_data['query']
self.concepts = # here is a long DB query; function(query)
return super(SearchView, self).form_valid(form)
[EDIT]
I did this:
def get_success_url(self):
return reverse('search', kwargs={'langcode': self.langcode})+"?query={}".format(self.query)
The form renders, but whenever I search for anything, I get back the empty search text field. And the URL looks something like this: http://localhost:8000/en-US/search/?query=asd
By default, a FormView (actually, any subclass of ProcessFormView) will return a HttpResponseRedirect in form_valid. As you are calling the super's method in your form_valid method, you also return a HttpResponseRedirect. In the process, the actual POST data is lost, and though you pass it as a GET parameter, it is not used in the actual form.
To fix this, you need to not call super in your form_valid method, but instead return a rendered template in a HttpResponse object, e.g.:
def form_valid(self, form):
self.query = form.cleaned_data['query']
self.concepts = # here is a long DB query; function(query)
return self.render_to_response(self.get_context_data(form=form))