Very simple question need a very simple solution. I need to add extra context to a profile view called profile_detail while still including the original context. Here is the userena view.
def profile_detail(request, username,
template_name=userena_settings.USERENA_PROFILE_DETAIL_TEMPLATE,
extra_context=None, **kwargs):
.........................
.........................
user = get_object_or_404(User,
username__iexact=username)
profile_model = get_profile_model()
try:
profile = user.get_profile()
except profile_model.DoesNotExist:
profile = profile_model.objects.create(user=user)
if not profile.can_view_profile(request.user):
return HttpResponseForbidden(_("You don't have permission to view this profile."))
if not extra_context: extra_context = dict()
extra_context['profile'] = user.get_profile()
extra_context['hide_email'] = userena_settings.USERENA_HIDE_EMAIL
return ExtraContextTemplateView.as_view(template_name=template_name,
extra_context=extra_context)(request)
I was told this would work. First I imported the userena view as userena_views. Then I tried to create my context, then send the request using the userena view and also change my urls to point to this view.
def profileview(request,username):
user=User.objects.get(username=username)
usergigs=Gig.objects.filter(user.id)
extra_context['usergig']=usergigs
return userena_views.profile_detail(request)
This didn't work, is this the right way? Is there an elegant way to do it? Or is my only option to copy the view into my views and edit from there?
Can you edit your code like this ?
def profileview(request,username):
user=User.objects.get(username=username)
usergigs=Gig.objects.filter(user.id)
extra_context['usergig']=usergigs
return userena_views.profile_detail(request, extra_context=extra_context)
Related
What I need
I'm developing a Pull Notification System to an existing Django Project. With there begin over 100+ views I'm looking to find a way to incorporate a argument(notification queryset) into all the views, as this is rendered to my base.html which I can do by passing it into a view's arguments dictionary.
Problem
I want to do this without editing all of the views as this would take a long time and would be required for all future views to include this variable.
What I've tried
Creating a template filter and pass in request.user as variable to return notification for that user. This works, however when the user selects a 'New' notification I want to send a signal back to the server to both redirect them to the notification link and change the status of viewed to 'True' (POST or AJAX). Which would required that specific view to know how to handle that particular request.
What I've considered
• Editing the very core 'View' in Django.
I've tried my best to explain the issue, but if further info is required feel free to ask.
Thanks!
models.py
class NotificationModel(models.Model):
NOTIFICATION_TYPES = {'Red flag':'Red flag'}
NOTIFICATION_TYPES = dict([(key, key) for key, value in NOTIFICATION_TYPES.items()]).items()
notification_type = models.CharField(max_length=50, choices=NOTIFICATION_TYPES, default='')
user = models.ForeignKey(User, on_delete=models.CASCADE)
created = models.DateTimeField(blank=True, null=True)
text = models.CharField(max_length=500, default='')
viewed = models.BooleanField(default=False)
views.py Example
class HomeView(TemplateView):
template_name = 'app/template.html'
def get(self, request, *args, **kwargs):
notifications = NotificationsModel.objects.filter(user=request.user)
args={'notifications':notifications}
return render(request, self.template_name, args)
def notification_handler(self, request)
if 'notification_select' in request.POST:
notification_id = request.POST['notification_id']
notification = NotificationModel.objects.get(id=notification_id)
notification.viewed = True
notification.save()
return redirect #some_url based on conditions
def post(self, request, *args, **kwargs):
notifications = self.notification_handler(request, *args, **kwargs)
if notifications:
return notifications
return self.get(self, request)
With there begin over 100+ views I'm looking to find a way to incorporate a argument(notification queryset) into all the views, as this is rendered to my base.html which I can do by passing it into a view's arguments dictionary.
You don't have to put it in the views (and actually, you shouldn't - views shouldn't have to deal with unrelated responsabilities) - you can just write a simple custom template tag to fetch and render whatever you need in your base template. That's the proper "django-esque" solution FWIW as it leaves your views totally decoupled from this feature.
Have you considered mixins ?
class NotificationMixin:
my_var = 'whatever'
class MyDjangoView(NotificationMixin,.....,View)
pass
Better, Using django builtins..
from django.views.generic import base
class NotificationMixin(base.ContextMixin):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['notification'] = 'whatever'
return context
and use this mixin with all your views AS THE FIRST CLASS INHERITED, See this.
For your case, Typing it instead of applying it to the base class is better, The base view class is not intended to be altered, It's meant to be extended, That's why we can solve the issue by this...
from django.generic.views import View as DjangoBaseView
from .mixins import NotificationMixin
class View(NotificationMixin, DjangoBaseView):
pass
and use any IDE to change all the imported Views to your View and not the django one.
I've been trying to create a Django generic deleteview, to delete an instance of a model.
I however have to check whether it is allowed to delete this item. This is done using a method defined in the model.
So far I've created this:
#login_required
def delete_employee(request, pk):
employee = None
try:
employee = Employee.objects.get(pk=pk)
except:
pass
if employee and not employee.empty():
return render(request, "error.html", None)
else:
# Load the generic view here.
Is this a decent way to go? And how can I load the generic view there?
I've tried things like EmployeeDelete.as_view() but those things don't work.
Or is there a way to check this in the generic view itself?
I've tried that as well, but I wasn't able to load an error page, just throw errors.
To do this with a DeleteView you can do this just by overriding the delete method on your inherited view. Here is an example based on what you have said. This is just an example of how you can accomplish it. You might need to tweak it for your exact scenario, specifically the else on can_delete
class EmployeeDeleteView(DeleteView):
success_url = reverse_lazy('index')
def delete(self, request, *args, **kwargs):
self.object = self.get_object()
can_delete = self.object.can_delete()
if can_delete:
return super(EmployeeDeleteView, self).delete(
request, *args, **kwargs)
else:
raise Http404("Object you are looking for doesn't exist")
I'm trying to crate Django web project , and there are some User backend application where every URL ( view ) only for authenticated users.
I can write a condition in every view for example:
User/urls.py
urlpatterns = patterns('',
url(r'^settings/', 'User.views.user_settings'),
url(r'^profile/', 'User.views.profile'),
#Other User Admin panel Urls
)
User/views.py
def user_settings(request):
if request.user is None:
return redirect('/login')
#Some other code
def profile(request):
if request.user is None:
return redirect('/login')
#Some other code
As you can see it's not well coded but it works fine.
The only thing that I want to know , is it possible to add some condition for urls.py file all urls for not adding same code lines in every view function.
For example Symfony , Laravel and Yii frameworks have something like that what I want to do.
Is it possible do this in Django ? :)
Edited Here
With #login_required I need to add it for every view, I need something for all urls for example:
In Symfony framework I can write
{ path: ^/myPage, roles: AUTHENTICATED } and every url like this /myPage/someUrl will be asked for authentication.
I believe that Django have something like that :)
Thanks.
Well if you use a class based view, it makes easier for you to add #login_required. For example you can create a class based view here:
class BaseView(TemplateView):
#method_decorator(login_required)
def dispatch(self, request, *args, **kwargs):
return super(BaseView, self).dispatch(*args, **kwargs)
Now override it each time you want to create a new view. For example:
class SettingsView(BaseView):
def get(request):
return (...)
It will check each time at url dispatch if the user is logged in.
Also, if you use a class based view, you can override get() to check if the user is authenticated.
class BaseView(TemplateView):
template_name= None
role= None
def get(self, request, *args, **kwargs):
if request.user is not None and role is "AUTHENTICATE":
return super(BaseView, self).get(request, *args, **kwargs)
else:
raise Exception('user is not logged in')
urls.py:
url(r'^settings/', BaseView.as_view(template_name='/sometemplate', role="AUTHENTICATE")
You can use the #login_required decorator:
https://docs.djangoproject.com/en/1.5/topics/auth/default/#the-login-required-decorator
From the docs:
login_required() does the following:
If the user isn’t logged in, redirect to settings.LOGIN_URL, passing the current absolute path in the query string. Example: /accounts/login/?next=/polls/3/.
If the user is logged in, execute the view normally. The view code is free to assume the user is logged in.
from django.contrib.auth.decorators import login_required
#login_required
def my_view(request):
...
You could use this : https://docs.djangoproject.com/en/1.6/topics/auth/default/#the-login-required-decorator
from django.contrib.auth.decorators import login_required
#login_required
def my_view(request):
...
I have a view where the user should be able to update an instance of a model, and also update or create new instances of a model related to the first one. I tryied using formsets to do this, and it works perfeclty for creating new objects, but i'm not finding a way to show the objects that already have been created. My problem is that i don't know how to populate the formsets with the existing data, so that i can put it in the context
So this are my models:
class Order(Model):
...
class invoice(Model):
order = models.ForeignKey(Order)
...
And my view is something like this:
class OrderDetailView(UpdateView):
invoice_form_class = InvoiceForm
def get_context_data(self, **kwargs):
context = super(OrderDetailView, self).get_context_data(**kwargs)
if not 'invoice_formset' in context:
context['invoice_formset'] = formset_factory(self.invoice_form_class, extra=3, can_delete=True, formset=BaseFormSet)
return context
There's probably an easy way to do this, but i'm not finding it anywhere
EDIT:
Thanks to #mariodev, i've learned about the inline_formsetfactory, and i'm using it. Now i can fill the formsets with the existing data, and i can create and alter existing ones, but when i try to delete them, nothing happens.
So now i'm defining this formset:
InvoiceFormset = inlineformset_factory(Order, Invoice, fields=('code',), can_delete=True, extra=0)
and my view looks like:
class OrderDetailView(UpdateView):
invoice_form_class = InvoiceForm
def get_context_data(self, **kwargs):
context = super(OrderDetailView, self).get_context_data(**kwargs)
if not 'invoice_formset' in context:
context['invoice_formset'] = InvoiceFormset(instance=self.get_object())
return context
def post(self, *args, **kwargs):
data = self.request.POST
order = self.get_object()
form = self.form_class(data)
invoice_formset = InvoiceFormset(data, instance=order)
if form.is_valid() and invoice_formset.is_valid():
self.object = form.save(order)
for f in invoice_formset:
f.save(self.object)
return HttpResponseRedirect(reverse('order_detail', kwargs={'order_id': self.get_object().order_id}))
I could add some extra lines in the post() to check if i have to delete the form, but it doesn't seem right for me to do it in the view. Is there something else i'm missing?
EDIT AGAIN:
Ended up finding this link which fix exactly this last problem i was having, so now it's all good!
I think it's better use normal function based views (FBV) for this. Understand what's going on first and then gradually move to CBV if you really need to.
This will help you with FBV:
http://catherinetenajeros.blogspot.com/2013/03/inline-formset-saving-and-updating-two.html
This may help you with CBV:
django class-based views with inline model-form or formset
I'm nearing what I think is the end of development for a Django application I'm building. The key view in this application is a user dashboard to display metrics of some kind. Basically I don't want users to be able to see the dashboards of other users. Right now my view looks like this:
#login_required
#permission_required('social_followup.add_list')
def user_dashboard(request, list_id):
try:
user_list = models.List.objects.get(pk=list_id)
except models.List.DoesNotExist:
raise Http404
return TemplateResponse(request, 'dashboard/view.html', {'user_list': user_list})
the url for this view is like this:
url(r'u/dashboard/(?P<list_id>\d+)/$', views.user_dashboard, name='user_dashboard'),
Right now any logged in user can just change the list_id in the URL and access a different dashboard. How can I make it so a user can only view the dashboard for their own list_id, without removing the list_id parameter from the URL? I'm pretty new to this part of Django and don't really know which direction to go in.
Just pull request.user and make sure this List is theirs.
You haven't described your model, but it should be straight forward.
Perhaps you have a user ID stored in your List model? In that case,
if not request.user == user_list.user:
response = http.HttpResponse()
response.status_code = 403
return response
I solve similiar situations with a reusable mixin. You can add login_required by means of a method decorator for dispatch method or in urlpatterns for the view.
class OwnershipMixin(object):
"""
Mixin providing a dispatch overload that checks object ownership. is_staff and is_supervisor
are considered object owners as well. This mixin must be loaded before any class based views
are loaded for example class SomeView(OwnershipMixin, ListView)
"""
def dispatch(self, request, *args, **kwargs):
self.request = request
self.args = args
self.kwargs = kwargs
# we need to manually "wake up" self.request.user which is still a SimpleLazyObject at this point
# and manually obtain this object's owner information.
current_user = self.request.user._wrapped if hasattr(self.request.user, '_wrapped') else self.request.user
object_owner = getattr(self.get_object(), 'author')
if current_user != object_owner and not current_user.is_superuser and not current_user.is_staff:
raise PermissionDenied
return super(OwnershipMixin, self).dispatch(request, *args, **kwargs)
You need to have some information stored about what list or lists a user can access, and then include that in the user_list lookup. Let's assume the simple case where List has a single owner, a foreign key to the User model. That's a many-to-one relationship between lists and users; no list is owned by more than one user, but a user can have multiple lists. Then you want something like this:
try:
user_list = models.List.objects.get(pk=list_id, owner=request.user)
except models.List.DoesNotExist:
raise Http404
Whether to return 404 or 403 is to some extent a matter of opinion; the definition for 403 says:
If the request method was not HEAD and the server wishes to make public why the request has not been fulfilled, it SHOULD describe the reason for the refusal in the entity. If the server does not wish to make this information available to the client, the status code 404 (Not Found) can be used instead.
http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.4
If you do return a 404, you can use the django shortcut function get_object_or_404 instead of the explicit try/except - there's nothing wrong with doing it explicitly, but the need is common enough that there's a convenience function to do it.