How do I check from the django code, which User is saving a model currently ?
I need to throw Validation Errors or assign some permissions to him from that.
Assuming you execute the model.save() from a view function, you can get the current user with request.user.
from django.contrib.auth.models import Permission
def myview(request):
model = Model(...)
model.save()
permission = Permission.objects.get(codename="...")
request.user.user_permissions.add(permission)
EDIT: Access the request in a form
The simplest way to get at the request from your form validation code is probably to set a attribute on the form instance:
def myview(request):
...
form = SomeForm(...)
form.request = request
Inside your form validation logic you can now use self.request to access the user:
class SomeForm(...):
def clean_somefield(self):
data = self.cleaned_data["somefield"]
if self.request.user....:
raise ValidationError()
Related
I am using a ModelViewSet to create objects from parameters received in a POST request. The serialiser looks like this:
class FooSerializer(ModelSerializer):
class Meta:
model = Foo
fields = '__all__'
I want to intercept the request, and perform a check on it (against a method of the model, if it matters) before allowing the creation to continue. In vanilla django forms, I would override the form_valid method, do the check, and then call super().form_valid(...). I am trying to do the same here:
class BookingView(ModelViewSet):
queryset = DirectBooking.objects.all()
serializer_class = DirectBookingSerializer
def create(self, request):
print(request.data)
#Perform check here
super().create(request)
This works, in the sense that it creates an object in the DB, but the trace shows an error:
AssertionError: Expected a `Response`, `HttpResponse` or `HttpStreamingResponse` to be returned from the view, but received a `<class 'NoneType'>`
This seems strange, as I would expect the super().save to return the appropriate response.
I am aware that I will need to return a response myself if the check fails (probably a 400), but I would still like to understand why it is failing here.
A view should return a HttpResponse. In a ViewSet, you do not implement .get(..) and .post(..) directly, but these perform some processing, and redirect to other functions like .create(..), and .list(..).
These views thus should then return a HttpResponse (or "friends"), here you call the super().create(request), but you forget to return the response of this call as the result of your create(..) version.
You should thus add a return statement, like:
class BookingView(ModelViewSet):
queryset = DirectBooking.objects.all()
serializer_class = DirectBookingSerializer
def create(self, request):
print(request.data)
#Perform check here
return super().create(request)
I am working in Django and I have a situation where I have written a custom validator that lives in the model.py
This validator should return a validationError when the input is bad.
In the project I am working on, we are using Django Rest Framework for our API and the Django admin panel for our admin panel. They connect to the same DB
My problem is that when the request comes from the API I need to return a 'serializers.ValidationError' (which contains a status code of 400), but when the request comes from the admin panel I want to return a 'django.core.exceptions.ValidationError' which works on the admin panel. The exceptions.ValidationError does not display correctly in the API and the serializers.ValidationError causes the admin panel to break. Is there some way I can send the appropriate ValidationError to the appropriate place?
here is my validation function (it lives in the model)
def validate_unique(self, *args, **kwargs):
super(OrganizationBase, self).validate_unique(*args, **kwargs)
qs = self.__class__._default_manager.filter(organization_type="MEMBER")
if not self._state.adding and self.pk is not None:
qs = qs.exclude(pk=self.pk)
if qs.exists():
raise serializers.ValidationError("Only one organization with \'Organization Type\' of \'Member\' is allowed.") #api
raise exceptions.ValidationError("Only one organization with \'Organization Type\' of \'Member\' is allowed.") #admin
Those two lines at the end are the two errors written together for illustration's sake, in this case only the #api one would run
Basically I want to send errorA when the request is coming from the admin panel and errorB when the request is coming from the API
Thank you
For raising different error classes write different validators.
rest framework api:
You can use the UniqueValidator or a custom validation function. check link [1]
eg:
class MySerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
fields = (....)
def validate(self, data):
# my validation code
raise serializers.ValidationError(....)
return data
admin panel:
for the admin panel you can use a custom form [2].
eg:
class MyForm(forms.ModelForm):
class Meta:
model = MyModel
def clean(self):
cleaned_data = super(MyForm, self).clean()
# my validation code
raise exceptions.ValidationError(....)
return cleaned_data
class MyAdmin(admin.ModelAdmin):
form = MyForm
In both the serializer and form you can access the instance object if not none.
[1] http://www.django-rest-framework.org/api-guide/validators/#uniquevalidator
[2] https://docs.djangoproject.com/en/1.11/ref/contrib/admin/#django.contrib.admin.ModelAdmin.form
I am new to django. I made a form. I want that if the form is filled successfully then django should redirect to a success page showing the name entered in the form but no parameters should be present in the url itself.
I searched on the internet and the solution I got was to redirect to url with pk as a get parameter which fetches the data and shows in the view. But I don't want to pass any thing in the url itself. and some websites say that http can't redirect with post data.
Here's my views.py
class UserRegistrationView(CreateView):
model = UserForm
template_name = 'userregistration.html'
form_class = UserForm
success_url = 'success'
def get_success_url(self):
return reverse('success',kwargs = {'name' : self.object.firstName})
and here's the template to which I want to redirect:
<h2>Congratualations for registering {{name}} </h2>
Basically what I want is that if the person fill form mentioning his/her firstName as "xyz" then the redirected success page should say that "Congratulations for registering xyz"
You can use django sessions, which I believe installed by default in 1.8
Look here
# Set a session value:
request.session["fav_color"] = "blue"
# Get a session value -- this could be called in a different view,
# or many requests later (or both):
fav_color = request.session["fav_color"]
# Clear an item from the session:
del request.session["fav_color"]
You can pass your pk via session and extract your object in another view without affecting your url.
Make sure you clean up after yourself.
Let me know if more help needed.
One of the possible ways of passing data between views is via sessions. So, in your UserRegistrationView you need to override the form_valid method as shown below.
class UserRegsitrationView(CreateView):
def form_valid(self,form):
self.request.session['name'] = self.object.firstName
return super(UserRegistrationView,self).form_valid(form)
class SuccessView(TemplateView):
template_name = "success_template.html"
def get_context_data(self,**kwargs):
context = super(SuccessView,self).get_context_data(**kwargs)
context['name'] = self.request.session.get('name')
del self.request.session['name']
return context
One more thing that you can modify in your code is that you need not declare success_url if you are overriding get_success_url
I am looking for a way to redirect users to different routes/templates that is compatible with using #view_config.
I have a function that reads in an uploaded file and attempts to create a new model based on the file content. I was wondering if there was a clean way that I can redirect the user to one of two urls, based on whether the creation of the new model succeeds or there is an error.
If the model creation is successful, I want to redirect the user to the model page. If there is an error, I want to redirect the user to an error page. However, am having trouble breaking out the original function (load_model)'s view_config when rendering the error page.
#view_config(renderer="error.mak")
#view_config(renderer="model.mak",
route_name='load_model_route')
def load_model(self):
...
model = Model.find_model(model_name)
if model:
#redirect to model_route
else:
#redirect to model_error_route
Each route would have a #view_config that binds it to a function.
What you are asking is not "How to redirect" but "How to change renderer in the view function". To answer quickly, I think you could use:
request.override_renderer = 'other-renderer.mak'
But I don't think it's a good idea. Here's the usual pattern that is used most of the time to handle form submission:
from pyramid.httpexceptions import HTTPFound, HTTPNotFound
from pyramid.url import route_url
from your_app import Model, some_stuff, save_to_db
#view_config(route_name='new_model',
renderer='model/new.mak', request_method='GET')
def new(request):
"""Shows the empty form."""
return {'model': Model(), 'errors': {}}
#view_config(route_name='create_model',
renderer='model/new.mak', request_method='POST')
def create(request):
"""Receives submitted form."""
model = some_stuff()
if len(model.errors) = 0: # is valid
# do your stuff, then redirect
save_to_db(model)
return HTTPFound(route_url('show_model', request, model_id=model.id))
# is invalid
return {'model': model, 'errors': model.errors}
#view_config(route_name='show_model',
renderer='model/show.mak', request_method='GET')
def show(request):
"""Shows details of one model."""
model = Model.find(request.matchdict['model_id'])
if model is None:
return HTTPNotFound()
return {'model': model}
In short:
You show an empty form when you have a GET on the route for a new model.
You handle the form submission (POST) in a different view function
If the data is valid, you do your stuff, then you redirect with HTTPFound
If the data is invalid, you return a dict to show the form again, with errors this time
You use the same renderer in the GET and POST, but the one in the POST is only used in case of invalid data (otherwise, you redirect).
You have another view function to show the created model.
Antoine showed a more general solution, but here's an attempt to stick to your basic format.
The idea is that you want to do some processing and then redirect the user to either a success or a failure page. You can redirect to a failure page if you want to just the same way you redirect to a success page, but I'll show a different version where you just show the error page in load, but if load works you redirect to the model.
config.add_route('show_model', '/models/{id}')
config.add_route('load_model', '/load_model')
#view_config(route_name='load_model', renderer='error.mak')
def load_model(self):
# ...
model = Model.find_model(model_name)
if model:
return HTTPFound(self.request.route_url('show_model', id=model.id))
return {} # some dict of stuff required to render 'error.mak'
#view_config(route_name='show_model', renderer='model.mak')
def show_model(self):
id = request.matchdict['id']
model = Model.get_model_by_id(id)
# be aware model could fail to load in this new request
return {'model': model} # a dict of stuff required to render 'model.mak'
I have a model (UserProfile) with which I want to record a user's IP address when new user accounts are created.
I've begin the process of overriding the Django model's save() method, but I am totally uncertain how to properly access HttpRequest attributes from within a model. Can any of you guys help? Google was unable to provide a specific answer for me.
You can get to the request from within the admin, but you can't do it in general code. If you need it then you'll have to assign it manually in the view.
You always need to pass on request information from the view to your save-method.
Consider that saving an instance doesn't always have to be invoked from a http request (for a simple example think of calling save in the python shell).
If you need to access the request object within the admin while saving, you can override it's save_model method:
def save_model(self, request, obj, form, change):
# do something with obj and request here....
obj.save()
But otherwise you always have to pass it on from the view:
def my_view(request):
obj = MyClass(ip_address = request.META['REMOTE_ADDR'])
Or to make this easier to re-use, make a method on the model like:
def foo(self, request):
self.ip_address = request.META['REMOTE_ADDR']
self..... = request.....
and call it from your view with obj.foo(request).