Validating a slug in Django - python

I'm guessing this is going to involve regexp or something, but I'll give it a shot. At the minute, a user can break a website by typing something similar to £$(*£$(£#$&£($ in the title field, which is converted into a slug using Django slugify.
Because none of these characters can be converted, Django returns an error. My question is, what should I put in the form validation method to raise a forms.ValidationError when the user uses a title like this?
Thanks.

This question is half a decade old so in updating my question I should explain that I'm at least nodding to the past where some features might not have existed.
The easiest way to handle slugs in forms these days is to just use django.models.SlugField. It will validate itself for you and imply that this field is an index.
If you're not using this on a model, you can still hook in the same validator that SlugField uses:
from django.core.validators import validate_slug
slug = forms.CharField(..., validators=[validate_slug])
If you just want to do behind-the-scenes checking or write your own validator, you can use a similar technique to pull in Django's definition of a valid slug. It's just the compiled regex that validate_slug above uses:
from django.core.validators import slug_re
if slug_re.match(...):
...
I can't imagine it will change, but by locking yourself to Django's idea of a slug, you'll ensure consistency if Django does change one day.

SLUG_REGEX = re.compile('^[-\w]+$')

Related

Conditional Logic in django forms

I'm trying to build a conditional form where selecting one option will cause a new field to appear underneath, while selecting another option will display something else.
Formstack has a good example https://www.formstack.com/features/conditional-logic
I couldn't find any preexisting form packages for django with this functionality. How should I start implementing this?
Django forms (especially if you use the ModelForm library) are a direct reflection of your Django application Model. You should therefore start by refactoring your Django application Model to have fields that have optional values (i.e. they can be NULL, empty or have a default value already created).
These would be the form fields that are shown/hidden based on your conditional(s) and they may or may not have values (if they are hidden based on a conditional it is impossible to provide a value to them so the Model fields must be able to accept NULL values or use the defaults).
You would then use a client-side language such as Javascript (JS) to handle the user iteraction with your Django application. A simple to use JS framework like jQuery would be worthwhile investigating for your needs.
In addition to the exceptional Django docs on Forms, I also highly recommend you take a look at Django Crispy Forms writtten by PyDanny to see how Django forms should be done right.

Manage slug for django

I try to use django-uuslug, to manage unique and unicode slug with django. This project seems interesting, it is better to use an existing project to reinvent the wheel. However I have a question, I would to know if it's be possible to specify one column more, to make the slugify on the current object and the optionnal column. For example if we have a site column and want have slug unique per site not per table.
uuslug(self.title, instance=self, unique_per_column=self.site)
Otherwise is there a better way to manage slug in django or not.
If you have a look at the source code of uuslug, it is possible to do what you want like this:
uuslug(self.title, instance=self, filter_dict={'site': self.site})
this will cause uuslug to filter the queryset of instance's model to rows with the same value of site field before doing the uniqueness check on that queryset

Does django-rest-swagger not work well with modelserializers?

I've been going off of the documentation on the django-rest-swagger github page, more specifically the part called "How it works". It shows that you can define your own parameters for your rest api, and have those parameters show up in your swagger doc page.
The commenting example is something like:
"""
This text is the description for this API
param1 -- A first parameter
param2 -- A second parameter
"""
I can get this to work, but my issue is how to specify if the variable is required, its parameter type, and its data type. The github page shows an example image of how your swagger doc could look, and they have the information I just mentioned. But when I comment my custom parameters like the example shows, my parameters just show as parameter type: "query", data type: is blank, and it doesn't show "required".
The closest thing I have found to an answer was in this stackoverflow question. It seems like an answer provider is saying that django-rest-swagger generates its documentation by automatically inspecting your serializers (which is fine), and that modelserializers won't contain enough information for django-rest-swagger to properly derive the criteria I mentioned above. I get that it can't figure out this criteria but there must be some way for me to manually specify it then.
Am I correct that django-rest-swagger would only display what I want if I rewrote my modelserializers as just serializers? Is there no way for me to manually tell django-rest-swagger what a parameter's parameter type and data type should be, and if it's required?
I know I must be missing something here. I use class-based views and modelserializers that are almost identical to the examples in the django-rest-framework tutorials. It seems entirely possible that I'm just missing an understanding of "parameter types" in this context. My API is working great and I don't want to rewrite my modelserializers to serializers just so I can get better automatic documentation through swagger.
ModelSerializers are the right way to go with DR-Swagger. It can be a bit tricky chasing down exactly where the different Swagger fields are extracted from though, I often had to fall back to step-debugging through the page rendering process in order to figure out where things were coming from.
In turn:
Required? comes from the Field.required parameter (either set on the model or the Serializer field).
Description comes from the Field.help_text parameter.
In the new-style DRF serialization, the description text comes from the ViewSet's docstring. If you want method-specific docs, you need to override the docstring for individual methods, e.g. retrieve:
def retrieve(self, request, *args, **kwargs):
"""Retrieve a FooBar"""
return super().retrieve(request, *args, **kwargs)
One thing to note is that DR-Swagger migrated to using the new DRF schema logic in version 2.0 (with DRF version 3.5), which has a few rough edges still. I recommend sticking with DR-Swagger version 0.3.x, which (though deprecated) has more features and in my experience, more reliable serialization.
In most cases ModelSerializer is what you need, because it can be greatly customized to suit your needs. In ideal situation you should define all your constraints, like required attribute on a field, in your model class, but there are times when it's not architecturally possible, then you can override such a field in your ModelSerializer subclass:
from django.contrib.auth import get_user_model
from rest_framework import serializers
class UserSerializer(serializers.ModelSerializer):
first_name = serializers.CharField(required=True)
last_name = serializers.CharField(required=True)
class Meta:
model = get_user_model()
In the example above I serialize standard User model from Django and override required attributes so, that first_name and last_name are now required.
Of course, there are cases when it's hard or impossible to use ModelSerializer then you can always fallback to Serializer subclassing
In the code you have:
"""
This text is the description for this API
param1 -- A first parameter
param2 -- A second parameter
"""
Try:
""" This text is the description for this API
param1 -- A first parameter
param2 -- A second parameter
"""
I have found some python and/or Django plugins need the docstring's first line, which is the one with the opening three double-quotes to also be the line that starts the documentation. You might even want to try no space between the last double-quote and the T.

How to filter autocompletion results in django grappelli?

We have a soft delete scheme where we just mark things as deleted and then filter the deleted ones out in various places. I'm trying to figure out how to filter the deleted ones out of the grapelli autocomplete suggestions.
In the end I went with this:
from grappelli.views.related import AutocompleteLookup
class YPAutocompleteLookup(AutocompleteLookup):
""" patch grappelli's autocomplete to let us control the queryset
by creating a autocomplete_queryset function on the model """
def get_queryset(self):
if hasattr(self.model, "autocomplete_queryset"):
qs = self.model.autocomplete_queryset()
else:
qs = self.model._default_manager.all()
qs = self.get_filtered_queryset(qs)
qs = self.get_searched_queryset(qs)
return qs.distinct()
It can be installed by overriding the relevant url:
url(r'^grappelli/lookup/autocomplete/$', YPAutocompleteLookup.as_view(), name="grp_autocomplete_lookup"),
Make sure this is ahead of Grappelli in your urls.
If your working with the Admin site, you should take advantage of the ModelAdmin.queryset function:
https://docs.djangoproject.com/en/1.5/ref/contrib/admin/#django.contrib.admin.ModelAdmin.queryset
As I found out, changing the default model manager to restrict the results is a bad idea, causing all kinds of nasty problems. For example: preventing syncdb, shell or shell_plus from running. Making it impossible to add the first record to a blank db. The exact errors depend upon what your restricting, but you are bound to get a few.
What is needed here is a way to tell Grappelli the name of the queryset manager to use. Passed in or a setting perhaps?
You can specify a simple (constant or related field) filter using ForeignKey.limit_choices_to. Grappelli grabs this value and sends it in the GET as param 'query_string'.
However, this might not be enough. I posted a request to the Grappelli repo I use to add a way to specify the record manger to use, or just automatically use the admin queryset (ModelAdmin.queryset).
My post is here:
https://github.com/sehmaschine/django-grappelli/issues/362
It looks like you can pass extra search params into the ajax autocompleter somehow. Likely a frontend hack needed.
https://github.com/sehmaschine/django-grappelli/blob/master/grappelli/views/related.py#L101
OR
You can make the default Manager for the models return an already filtered list, and have places that need to explicitly see deleted items remove that restriction.
This would likely make the default case much easier for you across the board.

Django ModelForm, having a foreign key as a hidden field

I'm basically building a very trivial form. Let's stick to the books/publisher examples given in the django tutorials and build upon that.
I have a user login to the web app, at which point the first thing they can do is click on a publisher. This publisher then gets saved for their session. Upon that I take them to a create book form. In there I embed the the publisher's id from the database into a hidden field.
Upon the user submitting an HTTP POST, I do something like:
mybookform = BookForm(request.POST)
if mybookform.is_valid():
abook = mybookform.save(commit=False)
abook.publisher_id = request.POST['publisher_id']
mybookform.save()
Yes there's a few naive things done here, such as blindly grabbing the publisher_id and verifying if it's indeed a real publisher id, amongst other security issues. Let's just not pay attention to that for the moment.
My question is, is there a better way of handling this? Although hypothetically this example doesn't make logistical sense, in my particular app the example actually makes sense. The problem is I get a ValueError exception saying publisher_id needs to be a Publisher instance.
Now I can easily retrieve a publisher instance with Publisher.objects.filter(id=..) and use that instead. The question is, is it really necessary? Can I avoid the additional query to the database and somehow update this form instance in a more 'elegant' fashion?
Also, is it possible to somehow embed the publisher in a hidden field so that I do not need to do mybookform.save(commit=False) and just do mybookform = BookForm(request.POST) followed by mybookform.save() immediately?
Retrieving the instance of the publisher does protect against client-side changes that might reference a completely invalid publisher.
To your second question, yes you can include that field as a hidden field by overriding the field in the ModelForm with the approriate form field setting the widget to HiddenInput.
There is no better way to do this.
I would use the get_object_or_404 function for this.
And yes, you can prevent this to be modified by the user by setting the model field to editable=False,

Categories

Resources