In my django app, I have some objects that cause the corresponding URL in the django admin to be non ascii. (for example: http://mysite/admin/myapp/myclass/Présentation/)
I can edit the object without any problem but when I save it I have the following error:
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe9' in position 24: ordinal not in range(128), HTTP response headers must be in US-ASCII format
The strange thing is that the object is correctly saved into the database.
Does anybody know how the Django admin manages unicode? Any info, pointer or idea that can help to fix this problem would be appreciated.
Thanks in advance
Update: Here is the code of the Model
class Plugin(models.Model):
"""Some subcontent that can be added to a given page"""
class Meta:
ordering = ['ordering']
name = models.CharField(max_length=32, primary_key=True)
div_id = models.CharField(default='rightcol', max_length=32)
published = models.BooleanField(default=True,
help_text=_("If this is not checked, it is not displayed on the page."))
ordering = models.IntegerField(default=1,
help_text=_("plugins are sorted with this number in ascending order"))
content = models.TextField(blank=True)
registration_required = models.BooleanField(_('registration required'),
help_text=_("If this is checked, only logged-in users will be able to view the page."))
def __unicode__(self):
return u"%s -- %s" % (self.name, self.div_id)
Update:
That's clear that non-ascii character are not recommended in an URL. That's the cause of my problem and I have changed that.
Does anybody know what is used by the Django admin to build the URL of an object. I guess that it is the primary key. Is it right? is there a way to force Django to use something else and to retrieve the object safely?
I'm pretty sure your database is probably using the latin1 encoding. Django supposes you have everything set as unicode (utf8).
To check this out, get into MySQL Shell and type:
mysql> show variables like 'char%';
If you see a bunch of latin1 (or any other encoding that isn't utf8, except binary), you'll have to do this:
Open up my.cnf and look for the [mysqld] section.
Make sure after tmpdir = /tmp, you have the following lines:
default-character-set=utf8
collation_server=utf8_unicode_ci
character_set_server=utf8
skip-external-locking
skip-character-set-client-handshake
Restart the server.
You'll have to recreate or manually edit the encoding of all databases and table you have, changing my.cnf only affects the databases that will be created.
Hope I've helped.
Edit: By the way, on which Django version are you on? Seems like this was a bug that was fixed on 1.1: http://code.djangoproject.com/ticket/10267
This link saved my day
you have to add unicode support for your admin in your models.py:
class MyModel (models.Model):
some_field = models.CharField(max_length=1000)
def __unicode__(self):
return u'%s'%(self.some_field)
there might be other problems such as: your system encoding is not utf8, your database encoding is not utf8 and ... which is mentioned in the link provided!
in your model, have you tried putting
class Model(models.Model):
def __unicode__(self):
return self.name ## having a model object named "name"
Not sure if this is the answer you are searhing for, but have you tried?
I am now using the default id as primary key for every class of my model. As a consequence, I don't have forbidden characters in the url when accessing objects from the admin site.
I do recommend to keep the default id as primary key in most cases
Related
When trying to save the data in the DB I have this error:
sqlite3.InterfaceError: Error binding parameter 1 - probably unsupported type.
models.py
class Movie(Model):
title = CharField(max_length=255)
omdb = JSONField()
slug = SlugField(max_length=255, unique=True, allow_unicode=True)
views.py
omdb_data = get_movie(title) # returns response.json() from external API call
print(type(omdb_data['Title'])) # str
print(type(omdb_data)) # dict
movie = Movie(title=omdb_data['Title'],
omdb=omdb_data, slug=slugify(title))
movie.save() # crashing here
What could be wrong? I'm guess it's problem with title or omdb parameters (not sure if ID counts or not) but no idea whats wrong.
SQLite doesn't support all types data. It's in its name (Lite). You may try to convert to PostgreSQL or another complete database solution. Here is a tutorial for Django+Postgres but be careful, it's little bit outdated.
I'm working on a Django app that will contain sensitive User data, so I'm taking a number of steps to anonymise User objects and their data.
I created custom 'User' object that subclassses the AbstractBaseUser model like so:
class User(AbstractBaseUser, PermissionsMixin):
(...)
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
(...)
It has the following linked ModelAdmin object:
from django.contrib.auth.forms import UserChangeForm
#admin.register(User)
class UserAdmin(admin.ModelAdmin):
form = UserChangeForm
I'm using UUID fields for primary keys to ensure maximum anonymity, yet I would like to be able to reset User passwords in the Django Admin (like you can do with the default User object)
However, when I open a User in the admin and press the link to change the password I get the following error message:
User with ID "21362aca-6918-47ea-9b29-275350a89c54/password" doesn't exist. Perhaps it was deleted?
The admin url is still expecting a url with the an integer as its pk value.
So it seems that I have to override the admin url configuration in the ModelAdmin definition, but I was wondering if there was a simpler way to achieve the desired result - as I imagine that replacing the User.pk with an UUID field is a fairly regular occurrence and I image many developers have ran into this problem. I tried to find some kind of settings / toggle to achieve this but to no avail, am I missing something?
Your 'UserAdmin' inherits from ModelAdmin, which provides the urls via the get_urls method for the add, change, delete, etc. views but also a 'fallback' url:
urlpatterns = [
#...
url(r'^(.+)/change/$', wrap(self.change_view), name='%s_%s_change' % info),
# For backwards compatibility (was the change url before 1.9)
path('<path:object_id>/', wrap(RedirectView.as_view(
pattern_name='%s:%s_%s_change' % ((self.admin_site.name,) + info)
))),
]
The url you are following looks like /user/<UUID>/password/ which only fits the regex of the fallback pattern - which redirects you to the change page in such a way that it uses <UUID>/password as the object_id.
Try inheriting from django.contrib.auth.admin.UserAdmin instead as its get_urls method provides the url pattern you need.
Some more poking around...
If your primary key field was an AutoField, the whole process would raise a ValueError('invalid literal for int') when trying to cast int('some_integer/password') in django.db.models.fields.AutoField.get_prep_value in order to prepare the value for a query. Understandable!
HOWEVER: UUIDField uses the get_prep_value method of the base class Field. Field.get_prep_value just simply returns the value without even checking (after all, validating the value should be the job of UUIDField). So you end up with a query that looks for the bogus uuid '<uuid>/password', which obviously doesn't exist.
I have a model whose Primary Key is a TextField. This is a minimalistic reproduction of my issue:
Model:
class Filename(models.Model):
path = models.TextField(primary_key=True)
Serializer:
class FilenameSerializer(ModelSerializer):
class Meta:
model = Filename
fields = '__all__'
View:
class FilenameViewSet(ModelViewSet):
queryset = Filename.objects.all()
serializer_class = FilenameSerializer
I'm using a DefaultRouter for URLs. Here is the problem: If I sent
{"path":"test"} with a POST /filename/ I can perfectly retrieve my object with GET /filename/test/ as you would expect. However, if I POST /filename/ something like {"path":"c:\\test"} I would expect either GET /filename/c%3A%5Ctest/ or GET /filename/c%3A%5C%5Ctest/ to be the proper way to get it, but none of those works. Does anybody knows what's going on?
Update: The webserver logs show Not Found: /filename/c:\test so it's being decoded properly at some moment. Maybe some URL regex issue?
The issue was not with any URL encoded in general, but with the dot (%2E) in particular. The DefaultRouter() does not match the dots by default. This behavior can be modified setting the lookup_value_regex attribute in the ViewSet.
class FilenameViewSet(ModelViewSet):
queryset = Filename.objects.all()
serializer_class = FilenameSerializer
lookup_value_regex = '[^/]+'
hey you have to define the lookupfield , you can overide the get_object change string format to a norma text
class FilenameViewSet(ModelViewSet):
queryset = Filename.objects.all()
serializer_class = FilenameSerializer
lookup_field = 'path'
def get_object(self):
try:
from urllib import unquote
except ImportError:
from urllib.parse import unquote
path = unquote(kwarhs['path'))
return self.get_queryset.get(path=path)
if im getting your question correctly . you problem is with json format .
On adding escape character \ before special characters would resolve your issue.
see this link below:
How to escape special characters in building a JSON string?
I have a similar coce:
def override_urls(self):
return [
url(r"^(?P<resource_name>%s)/(?P<slug>[\w\d_.-]+)/$" % self._meta.resource_name, self.wrap_view('dispatch_detail'), name="api_dispatch_detail"),
]
Which produces an URL like:
/api/v1/nodes/<slug>/
Everything fine except that self.get_resource_uri(bundle) returns /api/v1/nodes/<id>/ and I cannot compare the current URL with the resource URI effectively.
What am I doing wrong?
Solution: working code
I implemented the proposed solution here:
https://github.com/ninuxorg/nodeshot/blob/refactoring/nodeshot/core/base/resources.py
Any additional feedback for improvement is welcome.
You could override get_resource_uri on your resource to return the correct uri. After all, the correct one is the one with the slug since that is the (first) one captured by your resource.
Update
The right way to do this is actually to skip override_urls and put this on the Resource's Meta:
detail_uri_name = 'slug'
TLDR Background
I dug a bit deeper and it looks like there's a much better place to implement this. The default implementation of get_resource_uri (indirectly) calls self.detail_uri_kwargs and then reverse.
The default implementation of detail_uri_kwargs in ModelResource just looks up self._meta.detail_uri_name (whose name is not intuitive), and grabs that key off the model. detail_uri_name defaults to pk.
If you just provide a different name here, you can skip the override_urls and the get_resource_uri!
Something like this (building on the code linked in comments by the OP):
from tastypie.resources import ModelResource
from tastypie.bundle import Bundle
class BaseSlugResource(ModelResource):
""" Base Model Resource using slug urls """
class Meta:
abstract = True
detail_uri_name = 'slug'
I'm not sure off the top of my head whether resource Metas are inherited (I'm going to guess they're not), so this may not work as a base class. Luckily, the one line required is probably fine to paste into each Resource that needs it.
I'd like to clean it up with a working example even though the #dokkaebi's answer is marked (and partially) correct. The only missing part is you still have to prepend url that will be resolved for listing and such.
from tastypie.resources import ModelResource
from myapp.models import SomeModel
class BaseSlugResource(ModelResource):
""" Base Model Resource using slug urls """
class Meta:
queryset = SomeModel.objects.all()
detail_uri_name = 'slug'
def prepend_urls(self):
return [
url(r'^(?P<resource_name>%s)/(?P<slug>[\w\.-]+)/$' % self._meta.resource_name, self.wrap_view('dispatch_detail'), name='api_dispatch_detail'),
]
This will display the correct resource_uri's for listing as well as resource get. However, you'll most likely loose {schema} resource (i.e. /api/posts/schema/) as it's treated as a slug too.
I am trying to use django-datatrans to translate a MarkupField (from django-markitup) on a model. Both apps work correctly independently, but when I register datatrans to translate a MarkupField then I can't add objects in the admin anymore.
Relevant code:
from django.db import models
from markitup.fields import MarkupField
from datatrans.utils import register
class Work(models.Model):
title = models.CharField(max_length=500)
content = MarkupField(help_text=MARKDOWN_HELP)
class WorkTranslation(object):
fields = ('title', 'content')
register(Work, WorkTranslation)
When I try to add a new Work-object in the admin I get the following error:
'unicode' object has no attribute 'raw'
The error happens here, in the markitup-module (in the line rendered = render_func(value.raw):
.../lib/python2.7/site-packages/markitup/fields.py in pre_save
def pre_save(self, model_instance, add):
value = super(MarkupField, self).pre_save(model_instance, add)
rendered = render_func(value.raw)
setattr(model_instance, _rendered_field_name(self.attname), rendered)
return value.raw
Local vars when failing:
add: False
model_instance: <Work: This is the title>
value: u'This is the content.'
self: <markitup.fields.MarkupField: content>
I have tried to inspect the variable value when the Work class is not registered for translation. In that case (and then it does work correctly) it is not a unicode string but an instance of markitup.fields.Markup.
I have not been able to figure out why the type changes and I realize this question is pretty specific. But I hope someone has insights anyway..
Had the same issue with django-modeltranslation and django-markitup when testing it:
class ModelTests(TestCase):
def test_my_class(self):
self.assertRaises(IntegrityError, models.MyClass.objects.create)
It works for me with:
class ModelTests(TestCase):
def test_my_class(self):
with self.assertRaises(IntegrityError):
models.MyClass.objects.create(info='', info_de='')
Where my installed languages are en and de. My default language is en. info is my field with markitup and translation. (I'm testing here that a field is required on MyClass, therefore the IntegrityError.)
(Btw. this produces a slightly different error:
class ModelTests(TestCase):
def test_my_class(self):
self.assertRaises(IntegrityError, models.MyClass.objects.create(info=''))
Error:
AttributeError: 'NoneType' object has no attribute 'raw'
)
Maybe this helps someone.