In order to use UpdateView without pk in the url, I override the get_object function, however, when I test it, the form is not saved.
There are two models involved: user and Profile, with a OnetoOne relation.
My url is as following:
url(r'^profile/edit/$', profileviews.ProfileUpdateView.as_view(template_name="Pages_Profile/profileupdate.html"),name="profileupdate_URL"),
And the view class is:
class ProfileUpdateView(UpdateView):
model = Profile
form_class = UserProfileForm
def get_object(self, queryset=None):
return get_object_or_404(self.model, user=self.request.user)
#method_decorator(login_required)
def dispatch(self, request, *args, **kwargs):
return super(ProfileUpdateView, self).dispatch(request, *args, **kwargs)
I think the problem lays on the view, but I can't figure out.
Can anyone help me out?
Thanks.
There is a project called django-braces which includes a whole bunch of very useful generic view mixins to help with using Django CBV (class based views):
from braces.views import LoginRequiredMixin
class ProfileUpdateView(LoginRequiredMixin, UpdateView):
model = Profile
form_class = UserProfileForm
def get_object(self, queryset=None):
return get_object_or_404(self.model, user=self.request.user)
You can find more about it here.
Related
I tried to convert a cutoms decorator from a function to a Class Mixin because I use CVB and I wanted to inherit from this Mixin to check for users status for some pages. I have this decorator which check whether the user is authenticated or not, if the user is authenticated and tries to access a page that has this decorator it will be redirected to the dashboard if is not then il will have the access to acces the page. I wrote this class as a class version of the decorator and if the user is logged in then it works ok but if it it's not and tries to acces that page it give me this error:
ImproperlyConfigured at /auth/login/
UserLoginView is missing the permission_required attribute. Define UserLoginView.permission_required, or override UserLoginView.get_permission_required().
this is the decorator:
def is_authenticated(view_func):
def wrapper_func(request, *args, **kwargs):
if request.user.is_authenticated:
return redirect('dashboard')
else:
return view_func(request, *args, **kwargs)
return wrapper_func
class version:
class IsAuthenticatedMixin(PermissionRequiredMixin):
def dispatch(self, request, *args, **kwargs):
if request.user.is_authenticated:
return redirect('dashboard')
return super().dispatch(request, *args, **kwargs)
the views which inherits this mixin
class IndexFormView(IsAuthenticatedMixin, CreateView):
permission_required = 'view'
template_name = 'home/index.html'
form_class = NewsletterForm
def post(self, request, *args, **kwargs):
email = request.POST['email']
if Newsletter.objects.filter(email=email).exists():
messages.warning(
request, 'This email is already subscribed in our system')
else:
Newsletter.objects.create(
email=email)
messages.success(request,
'Your email was subscribed in our system, you\'ll hear from us as soon as possible !')
return super().post(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['testimonials'] = Testimonial.objects.order_by('-created_date')[:9]
return context
class AboutTemplateView(IsAuthenticatedMixin, TemplateView):
template_name = 'home/about.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['banner_page_title'] = 'About Us'
context['page_location'] = 'home / about'
return context
class UserResgistrationCreateView(IsAuthenticatedMixin, CreateView):
template_name = 'home/auth/register.html'
form_class = UserRegistrationForm
success_url = reverse_lazy('login')
class UserLoginView(IsAuthenticatedMixin, LoginView):
template_name = 'home/auth/login.html'
The reason this happen is because you subclass from the PermissionRequiredMixin [Django-doc], this thus means that in all views where you use IsAuthenticatedMixin, you need to provide a permission_required class attribute. The error here is about the UserLoginView:
class UserLoginView(IsAuthenticatedMixin, LoginView):
template_name = 'home/auth/login.html'
permission_required = …
where you thus need to enter a value for the …. But the same problem will occur at other class-based views where you sublcass from IsAuthenticatedMixin, and you do not provide a value for permission_required.
It is however why you write a custom mixin here. What you are doing is implemented by the LoginRequiredMixin [Django-doc]. The only thing that is different is the redirect url, but you can specify this:
from django.contrib.auth.mixins import LoginRequiredMixin
from django.urls import reverse_lazy
class IsAuthenticatedMixin(LoginRequiredMixin):
login_url = reverse_lazy('dashboard')
redirect_field_name = None
In the view where you need to use a permission, you can also subclass the PermissionRequiredMixin:
from django.contrib.auth.mixins import PermissionRequiredMixin
class IndexFormView(IsAuthenticatedMixin, PermissionRequiredMixin, CreateView):
permission_required = 'view'
template_name = 'home/index.html'
form_class = NewsletterForm
When using function based views in Django, I normally would check for user authentication with:
if not self.request.user.is_authenticated:
return HttpResponseRedirect('/path/to/redirect')
Now I am trying to use Class based views instead but I don't know how to implement the authentication if I am using the get_context_data() method instead of just get().
class MyGenericView(generic.ListView):
model = models.MyModel
template_name = 'path/to/template.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['foo'] = 'bar'
return context
You want to override the dispatch method.
class MyView(ListView):
def dispatch(self, request, *args, **kwargs):
if not request.user.is_authenticated:
return HttpResponseRedirect('/path/to/redirect')
return super().dispatch(request, *args, **kwargs)
More details in the doc.
You can use LoginRequiredMixin in the view to check if the user is authenitcated:
from django.contrib.auth.mixins import LoginRequiredMixin
class MyView(LoginRequiredMixin , ListView):
login_url = “login_url_name”
# rest of the code
You can also use user_passes_test, for example, in urls:
def user_authenticated(user):
return user.is_authenticated
urlpatterns = [
path(
'',
user_passes_test(user_authenticated, login_url='login')(
MyView.as_view()
),
)
]
Context: how I handled foreign keys restrictions on GET
I have some trouble validating this form:
class RecordConsultationForm(forms.ModelForm):
class Meta:
model = Consultation
fields = ["fk_type", "fk_client", "duration", "date"]
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user', None)
super(RecordConsultationForm, self).__init__(*args, **kwargs)
self.fields['fk_client'].queryset = Client.objects.filter(fk_owner=self.user) # <=====HERE
The queryset restricts the available clients to users. Pretty effective, I just had to add the following to get_context_data():
#method_decorator(login_required, name='dispatch')
class BrowseConsultations(BrowseAndCreate):
template_name = "console/browse_consultations.html"
model = Consultation
form_class = RecordConsultationForm
success_url = 'browse_consultations'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['form'] = self.form_class(user = self.request.user) #<=====HERE
return context
def post(self, request):
form = self.form_class(user = self.request.user) #<=====HERE
return super().post(request)
Form validation on BrowseConsultations
Despite what I added in get_context_data() and post(), form data on POST does not seem valid, as if user were None (so it seems):
Invalid fk_client: Select a valid choice. 2 is not one of the available choices.
Maybe get_context_data() was not the proper place to set the user? Is there a way I could tweak the form in post()? Is it the proper way to do it?
Additional details
BrowseAndCreate BrowseConsultations inherits from is a Mixing class I created to handle common ordering tasks and messages. Here is a portion of it:
#method_decorator(login_required, name='dispatch')
class BrowseAndCreate(CreateView, TemplateView):
"""display a bunch of items from a model and the form to create new instances"""
def post(self, request):
super().post(request)
return redirect(self.success_url)
def form_valid(self, form):
super().form_valid(form)
messages.add_message(self.request, messages.SUCCESS,
"Recorded in {}".format(self.object.status))
def form_invalid(self, form):
for e in form.errors.items():
messages.add_message(self.request, messages.WARNING,
"Invalid {}: {}".format(e[0], e[1][0]))
Environment
django 3.0.4
python 3.7
First of all, the CreateView (that your BrowseAndCreate inherits from) handles form validation in the post method, where it calls form_valid on success and form_invalid on failure. Both these methods should return an HTTP response.
Furthermore, the get_context_data from FormMixin that you are overriding already takes care of getting the form data.
If you need the user in your form, you could have this in your form:
class RecordConsultationForm(forms.Form):
def __init__(self, user, *args, **kwargs):
self.user = user
super().__init__(*args, **kwargs)
And this in your view:
class BrowseConsultations(BrowseAndCreate):
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
I'm trying to build some frontend stuff in DRF - react. someAPI should run a query against db using current logged user id as parameter. After some hours trying here and there, following code works, but Im not sure is the right way to do it as involves using mixins and function overriding.
Question is, How can I achieve same result using generic DRF views?
class someAPI(mixins.ListModelMixin,
generics.GenericAPIView):
serializer_class = someSerializer
def get(self, request, *args, **kwargs):
customRole = get_object_or_404(Role, user=request.user)
self.queryset = ClassDependingOnRole.objects.filter(role=customRole.id)
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
You can use ListCreateAPIView directly.It is more useful.
class SomeApi(generics.ListCreateAPIView):
serializer_class = SomeSerializer
def get_queryset(self):
role=get_object_404(Role, user=self.request.user)
return ClassDependingOnRole.objects.filter(role=role)
It generates post and get methods automatically and validates serializer validations.You da find more detail from here about ListCreateAPIView.
I have two models Restaurant and Details. The superuser assigns each restaurant a user.When that user logs into admin i want only those Details associated with that user's Restaurant to be shown,and he should be able to edit them as well.
I tried to override admin's queryset function but to no success.Any help would be appreciated. This is what i did so far
I am just a beginner in Django.
class RestaurantAdmin(admin.ModelAdmin):
model = Details
def save_model(self, request, obj, form, change):
obj.user = request.user
super(RestaurantAdmin, self).save_model(request, obj, form, change)
def queryset(self, request):
print(request.user)
qs = super(ResaturantAdmin, self).queryset(request)
# If super-user, show all comments
if request.user.is_superuser:
return qs
return qs.filter(owner=request.user)
admin.site.register(Restaurant)
admin.site.register(Details,RestaurantAdmin)
The method you need to override is called get_queryset, not queryset.