Clean request.data in django rest - django

Quick question:
I know that Django has some baked-in security at different system levels, but I'm not sure if accessing the the request.data property directly safe? (can it cause a security vulnerability by simply reading the users input data before i have done something to clean it)
book = Book.objects.get(uuid=request.data.uuid)
(obviously, this is a simplified example)
Do I have to clean the data? Are there some packages that do this for me or can I use some of Django's native functions for this?
Thanks!

Since you mentioned you are using django rest framework, you should use the serializers for that.
Have a look at:
https://www.django-rest-framework.org/api-guide/serializers/
serializer = MySerializer(data=request.data)
serializer.is_valid(raise_exception=True)

Related

add_error(fieldname, ...) in Django Rest Framework (DRF)

I have a model serializer like this:
class MySerializer(ModelSerializer):
class Meta:
model = MyModel
fields = ('myfield', 'otherfield')
I need to check a cross-field condition, say, x.myfield > x.otherfield.
(There are actually several otherfields and several conditions.)
I need detailed and easy-to-grasp human-readable error messages.
I currently generate them (actually only the first) in MySerializer.validate() via raise ValidationError(message), but then the message comes under the non-field-errors key, which is ugly and difficult for the users. It would be much nicer to attach it to myfield.
In Django forms, I would use add_error('myfield', ...), but I
could not find a similar thing in rest framework.
What is a suitable idiom here?
Simple!
raise ValidationError(dict(myfield=[message]))
This way, one can mention multiple fields overall and can have multiple messages per field.
Where to find it
As of 2021-05, the respective information comes under
Overriding serialization and deserialization behavior in the documentation, not under "Validation" as one might expect.
(Why do I so often have to write a near-complete stackoverflow post before I finally find what I'm looking for in the documentation? I don't know. Hope it helps others now.)

Django full_clean method and data security

I need to take raw JSON data and put it direct into a Django model by using the Mymodel.objects.create( ... ) method. If I then run full_clean() on the instance created, is it then secure in terms of potential SQL injection or any malicious data that could potentially be injected?
The reason that I am not using a form is that the logic on the form is quite complex in that it dynamically builds a form so I need to post data in json format. I don't want to use the rest api as there is just one page where it has this complexity.
.full_clean(…) [Django-doc] does not perform any checks on SQL injection, nor does the Django ORM, since it simply escapes all parameters, so even if the data contains SQL statements, these are escaped, and therefore, normally, SQL injection is not possible.
But you need to run full_clean to validate data integrity. If you define constraints on your model, these are normally validated at the full_clean part. The ORM does not run these queries.
You thus can work with:
obj = Mymodel(**data)
obj.full_clean()
obj.save()
The reason that I am not using a form is that the logic on the form is quite complex in that it dynamically builds a form.
A form can remove a lot of boilerplate code, since it does not only performs validation, but also cleaning, it makes error messages more convenient, etc.
Django ORM protects the database from SQL-injections, but you are responsible for the output. For convenient data cleaning, I recommend using a DRF Serializers

Where to place helping functions in Django 1.5

I have a function "GenerateUsername" which returns a random username. I use it in a manager in managers.py, but where would be the best place to have this function? In a separate file called helpers.py?
def GenerateUsername():
username = str(random.randint(0,1000000))
try:
User.objects.get(username=username)
return GenerateUsername()
except User.DoesNotExist:
return username;
I think a more comprehensive answer is deserved here. There are several ways to go about it.
utils.py for the whole django site: This has the flaw that all django apps in that site will dump their generic functions in here and this file would become a blob
utils.py for each django app inside the site: So if you have two apps, app1 and app2, then you have app1/utils.py and app2/utils.py. This is slightly better than (1)
Library outside of Django (maybe on the same level as Django): I often make libraries for generic Django APIs in case they may be used by more than 1 site. This has the additional advantage that if you launch a new Django site tomorrow, you could use this generic function in that site as well just by importing this library after having it on your path. Inside the library, you could have separate modules such as userutils.py, mailutils.py which will address the 'blob' issue and also organize your code better. I recommend this approach.

Django 1.4 formwizard TestCases

I'm trying to write tests for form wizard views django.contrib.formstools.wizard.views.CookieWizardView, and I'm not sure how to handle writing the sequential posts in the test cases:
#test_views.py
def test_wizard_pass(self):
response = self.c.post('/wizard/url/',first_form_post_dict)
self.assertContains(...)
response = self.c.post('/wizard/url/',second_step_post_dict)
self.assertRedirect(...)
I assume I need to change the second post data based on something from the first response or something to do with the cookie used for session management, I'm just not sure what.
The test cases for CookieWizardView can be found in django.contrib.formtools.tests.wizard.wizardtests.tests (line 216, view source here), including multiple sequential posts. You can study how these are implemented and implement your own test cases in a similar way.

Django - How to pass dynamic models between pages

I have made a django app that creates models and database tables on the fly. This is, as far as I can tell, the only viable way of doing what I need. The problem arises of how to pass a dynamically created model between pages.
I can think of a few ways of doing such but they all sound horrible. The methods I can think of are:
Use global variables within views.py. This seems like a horrible hack and likely to cause conflicts if there are multiple simultaneous users.
Pass a reference in the URL and use some eval hackery to try and refind the model. This is probably stupid as the model could potentially be garbage collected en route.
Use a place-holder app. This seems like a bad idea due to conflicts between multiple users.
Having an invisible form that posts the model when a link is clicked. Again very hacky.
Is there a good way of doing this, and if not, is one of these methods more viable than the others?
P.S. In case it helps my app receives data (as a json string) from a pre-existing database, and then caches it locally (i.e. on the webserver) creating an appropriate model and table on the fly. The idea is then to present this data and do various filtering and drill downs on it with-out placing undue strain on the main database (as each query returns a few hundred results out of a database of hundreds of millions of data points.) W.R.T. 3, the tables are named based on a hash of the query and time stamp, however a place-holder app would have a predetermined name.
Thanks,
jhoyla
EDITED TO ADD: Thanks guys, I have now solved this problem. I ended up using both answers together to give a complete answer. As I can only accept one I am going to accept the contenttypes one, sadly I don't have the reputation to give upvotes yet, however if/when I ever do I will endeavor to return and upvote appropriately.
The solution in it's totality,
from django.contrib.contenttypes.models import ContentType
view_a(request):
model = create_model(...)
request.session['model'] = ContentType.objects.get_for_model(model)
...
view_b(request):
ctmodel = request.session.get('model', None)
if not ctmodel:
return Http404
model = ctmodel.model_class()
...
My first thought would be to use content types and to pass the type/model information via the url.
You could also use Django's sessions framework, e.g.
def view_a(request):
your_model = request.session.get('your_model', None)
if type(your_model) == YourModel
your_model.name = 'something_else'
request.session['your_model'] = your_model
...
def view_b(request):
your_model = request.session.get('your_model', None)
...
You can store almost anything in the session dictionary, and managing it is also easy:
del request.session['your_model']