i have a view method such as
#login_required(login_url='/users/login')
def my_submission(request,submission_id):
submission = Submission.objects.get(pk=submission_id)
return render(request, "assignments/mysubmission.html", {
"submission": submission
})
I was wondering if there is a way to pass submission_id which is the second param to user passes test decorator so that i can do something like
#user_passes_test(lambda u: u.id == Submission.objects.get(pk=submission_id).user.id, login_url='/')
thanks for the guide in advance.
You should just write it as a check in the view:
#login_required(login_url='/users/login')
def my_submission(request,submission_id):
submission = Submission.objects.get(pk=submission_id)
if submission.user_id != request.user.pk:
return redirect('/')
return render(request, "assignments/mysubmission.html", {
"submission": submission
})
Update
If you use this many times, consider implementing it is a class-based view, so you could inherit and utilize some extra features:
class UserIsOwnerMixin(AccessMixin):
"""Verify that the user the owner of related object."""
owner_id_field = 'user_id'
def dispatch(self, request, *args, **kwargs):
if not request.user.is_authenticated or getattr(self.get_object(), self.owner_field) != request.user.pk:
return self.handle_no_permission()
return super().dispatch(request, *args, **kwargs)
class SubmissionView(UserIsOwnerMixin, DetailView):
template = "assignments/mysubmission.html"
model = Submission
context_object_name = "submission"
login_url = '/users/login'
Related
I am trying to build CBV with class View parent. This view takes slug of object and find that object between two django models. The functions from services.py was doing a lot of DB queries, so, I tried to reduce them by giving to FeedbackSection necessary attributes(slug, model_instance and context) and lately override them in get method.
class FeedbackSection(View):
"""
Feedback section for different objects.
This is something like 'generic' view, so I implement it that it will find
the model and feedbacks for this model by having only slug.
"""
template_name = 'feedbacks/feedback-section.html'
form_class = CreateFeedbackForm
slug = None
model_instance = None
context = None
def get(self, request, *args, **kwargs):
self.slug = kwargs.get('slug')
self.model_instance = get_model_instance(self.slug)
self.context = get_feedback_section_context(self.slug, self.form_class, self.model_instance)
return render(request, self.template_name, self.context)
#method_decorator(login_required)
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
# will create feedback object and update model[Advert, Company] rating.
end_feedback_post_logic(self.request.user, form, self.model_instance)
return render(request, self.template_name, self.context)
The attributes(slug, model_instance and context), when post method is in runtime are equivalent to None.
The problem is that this implementation was working fine yesterday, but today it's not.
I know I can use my functions again, but in post method. I don't want to do this. Because it will multiple DB Queries by two.
We need to override the setup method of the View class and define those attributes there.
def setup(self, request, *args, **kwargs):
self.slug = kwargs.get('slug')
self.model_instance = get_model_instance(self.slug)
self.context = get_feedback_section_context(
self.slug,
self.form_class,
self.model_instance
)
return super().setup(request, *args, **kwargs)
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'm rewriting my function to class based views, this is the function I currently have.
#login_required
def invoice(request, invoice_no, template_name="invoice.html"):
context = {}
invoice_exists = Invoice.objects.filter(invoice_no=invoice_no)
if invoice_exists:
context['invoice'] = invoice_exists.first()
else:
return HttpResponseRedirect(reverse('invoices'))
return render(request, template_name, context)
you have to be logged in, it filters using a filter named invoice_no
path('invoice/<int:invoice_no>', views.InvoiceView.as_view(), name="invoice"),
and if a match is found returns it, if not redirects you back to the invoices page.
this is what I have as a class
class InvoiceView(DetailView):
queryset = Invoice.objects.all()
context_object_name = 'invoice'
pk_url_kwarg = 'invoice_no'
template_name = "invoice.html"
#method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super().dispatch(*args, **kwargs)
def get_object(self):
obj = super().get_object()
return obj
also the get object or 404 will do also since all it needs is a 404 page and it'll work.
Try this:
class ArticleDetailView(LoginRequiredMixin, DetailView):
template_name = "invoice.html"
context_object_name = 'invoice'
model = Invoice
def dispatch(self, request, *args, **kwargs):
try:
return super().dispatch(request, *args, **kwargs)
except Invoice.DoesNotExist:
return HttpResponseRedirect(reverse('invoices'))
def get_object(self):
return Invoice.objects.get(invoice_no=self.kwargs['invoice_no'])
Adjust according to your code.
from django.contrib.auth.mixins import LoginRequiredMixin
class InvoiceView(LoginRequiredMixin, DetailView):
template_name = "invoice.html"
context_object_name = 'invoice'
def get_queryset(self, *args, **kwargs):
invoice = get_object_or_404(Invoice, invoice_no=kwargs['invoice_no'])
return invoice
This will however return a 404 page if no data is found, if you like it to redirect it to invoices page, use a filter. Then use an IF statement to compare length > 0, if 0 then just redirect to page. Might as well put a message error then too.
I am trying to implement a template for adding VIEW, ADD and EDIT webpages with pluggable views. How do I use a url value like ?
This is the code I am trying to translate into pluggable views.
#app.route('/edit/category/<category>', methods=['GET', 'POST'])
def editCategory(category):
form = forms.AddCategory()
form.name.data = category
if form.validate_on_submit():
newName = form.name.data
database.editCategory(name = category, newName = newName)
#view single category?
return redirect('/view/categories/')
return render_template('edit-category.html', category = category, form = form)
Pluggable View Code
class ListView(View):
def getTemplate_name(self):
raise NotImplementedError()
def render_template(self, context):
return render_template(self.get_template_name(), **context)
def dispatch_request(self):
context = self.get_context()
return self.render_template(context)
class CategoryView(ListView):
def get_template_name(self):
return 'categories.html'
def get_objects(self):
return models.Category.query.all()
def get_form(self):
return forms.AddCategory()
def get_context(self):
return {'categories': self.get_objects(), 'form': self.get_form()}
app.add_url_rule('/view/categories', view_func=CategoryView.as_view('category'))
class EditCategory(ListView):
def get_template_name(self):
return 'edit-category.html'
def get_form(self, category):
form = forms.AddCategory()
form.name.data = category
return form
def get_context(self):
return {'form': self.get_form()}
app.add_url_rule('/edit/category/<category>', view_func=EditCategory.as_view('category'))
You need to override the dispatch_request method in the EditCategory class as this method has the url values passed to it.
class EditCategory(View):
...
def get_context(self, category):
return {'form': self.get_form(category)}
def dispatch_request(self, category):
context = self.get_context(category)
return self.render_template(context)
app.add_url_rule('/edit/category/<category>', view_func=EditCategory.as_view('category'))
As an aside, I think you are better off with the original decorated view functions in this case is there is very little commonality between the viewCategory and editCategory functionality
I am trying to build a django form wizard to allow people to register for an
event. I can get through the form wizard and see data in the done method.
The problem is that I also need event_id passed into done also. How do I get
event_id from the url through the form wizard and into done? Simple example?
------- urls.py ---------
named_register_forms2 = (
('basicdata', SeatsForm),
('form2', AnotherForm),
)
urlpatterns = patterns('',
url(r'^register/(?P<event_id>\d+)/$', register_wizard, name='register_step'),
)
------ forms.py -----------
class SeatsForm(forms.ModelForm):
class Meta:
model = MyModel
fields = [ 'last_name', 'first_name', 'address1', 'address2',
'city', 'state', 'zipcode', 'phone_number', 'email']
def __init__(self, *args, **kwargs):
super(SeatsForm, self).__init__(*args, **kwargs)
class RegisterWizard(SessionWizardView):
#storage_name = 'formtools.wizard.storage.session.SessionStorage'
template_name = 'wizard_form.html'
def done(self, form_list, **kwargs):
data = {}
for form in form_list:
data.update(form.cleaned_data)
print data
# I need event_id right here. How to get it?
return render_to_response('done.html', {
'form_data': [form.cleaned_data for form in form_list],
})
I think you will have to put that in the form and get it from there.
If its model form you can pass instance_dict param to the wizard view. instance_dict param. However in that case you will have to implement a wrapper view that will prepare the wizard view with these params. Something like this:
def wrapper_view(request, id):
#somecode
seats_instance = SeatsModel.objects.get(id=id)
another_instance = AnotherModel.objects.get(id=id)
inst_dict = { '0': seats_instance,
'1': another_instance
}
return RegisterWizard.as_view(named_register_forms2, instance_dict=inst_dict)(request)
class RegisterWizard(SessionWizardView):
#storage_name = 'formtools.wizard.storage.session.SessionStorage'
template_name = 'wizard_form.html'
def done(self, form_list, **kwargs):
data = {}
seatform= form_list[0]
seatinst = form.save()
#save other forms
...
#using seatinst get event id
return render_to_response('done.html', {
'form_data': [form.cleaned_data for form in form_list],
})
The question is two years old, but for completeness, I've been solving this problem by overriding the dispatch method of the wizard class. This method is called by the wizard with the raw arguments from the URL dispatcher. You can modify the wizard's instance_dict (and presumably any other wizard member) before anything else happens.
class RegisterWizard(SessionWizardView):
#storage_name = 'formtools.wizard.storage.session.SessionStorage'
template_name = 'wizard_form.html'
def dispatch(self, request, id, *args, **kwargs):
self.instance_dict = {
'0': SeatsModel.objects.get(id=id),
'1': AnotherModel.objects.get(id=id),
}
return super(RegisterWizard, self).dispatch(request, *args, **kwargs)
def done(self, form_list, **kwargs):
data = {}
seatform= form_list[0]
seatinst = form.save()
#save other forms
...
#using seatinst get event id
return render_to_response('done.html', {
'form_data': [form.cleaned_data for form in form_list],
})
I'm not sure there is any major functional advantage, but it feels like the wizard is a little more encapsulated doing it this way. In addition I've no idea if this was the intended use of the dispatch method.
I suppose if one were to inherit RegisterWizard the behaviour of setting instance_dict with objects from SeatsModel and AnotherModel would be available without the need to make user of a wrapper function; that might be the only actual advantage of doing it this way.