In a current Django project I get a django.db.utils.ProgrammingError every time I run test trough django-admin test — but only if I use the MySQL backend (using mysqlclient for Python 3); the tests run fine if I use the SQLite backend:
django.db.utils.ProgrammingError: (1064, "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'None, None) NOT NULL)' at line 1")
See below for a full traceback.
The project itself and the admin are running fine — with both backends, despite the tests bailing and with the build in server, as well as a WSGI app. The error only happens when running the tests.
I have a models.py in my core package, that defines some abstract models and mixins that are used by my Django apps. There's also a test.py in that package, in which some models are defined, that use those abstract classes and mixins, so I can test them. I guess the error is there… somewhere; but the I can't figure it out from the exception.
Since the "real" models are only in test.py and not in models.py, I have no clue on how to inspect the raw SQL with django-admin sql or similar…
Full Traceback
$ django-admin test blog -v 3
Creating test database for alias 'default' ('yogalessontv_test')...
Got an error creating the test database: (1007, "Can't create database 'yogalessontv_test'; database exists")
Type 'yes' if you would like to try deleting the test database 'yogalessontv_test', or 'no' to cancel: yes
Destroying old test database 'default'...
Operations to perform:
Synchronize unmigrated apps: streambox, profiles, core, suit, contact, allauth, messages, staticfiles, debug_toolbar
Apply all migrations: admin, auth, carousel, sites, pages, socialaccount, blog, contenttypes, account, sessions, videos
Synchronizing apps without migrations:
Running pre-migrate handlers for application core
Running pre-migrate handlers for application pages
Running pre-migrate handlers for application blog
Running pre-migrate handlers for application videos
Running pre-migrate handlers for application carousel
Running pre-migrate handlers for application suit
Running pre-migrate handlers for application admin
Running pre-migrate handlers for application auth
Running pre-migrate handlers for application contenttypes
Running pre-migrate handlers for application sessions
Running pre-migrate handlers for application sites
Running pre-migrate handlers for application allauth
Running pre-migrate handlers for application account
Running pre-migrate handlers for application socialaccount
Running pre-migrate handlers for application debug_toolbar
Creating tables...
Creating table core_trimstringsmixintestmodel
Creating table core_playtimemixintestmodel
Traceback (most recent call last):
File "/home/xxx/virtualenv/myproject/lib/python3.4/site-packages/django/db/backends/utils.py", line 62, in execute
return self.cursor.execute(sql)
File "/home/xxx/virtualenv/myproject/lib/python3.4/site-packages/django/db/backends/mysql/base.py", line 124, in execute
return self.cursor.execute(query, args)
File "/home/xxx/virtualenv/myproject/lib/python3.4/site-packages/MySQLdb/cursors.py", line 226, in execute
self.errorhandler(self, exc, value)
File "/home/xxx/virtualenv/myproject/lib/python3.4/site-packages/MySQLdb/connections.py", line 36, in defaulterrorhandler
raise errorvalue
File "/home/xxx/virtualenv/myproject/lib/python3.4/site-packages/MySQLdb/cursors.py", line 217, in execute
res = self._query(query)
File "/home/xxx/virtualenv/myproject/lib/python3.4/site-packages/MySQLdb/cursors.py", line 378, in _query
rowcount = self._do_query(q)
File "/home/xxx/virtualenv/myproject/lib/python3.4/site-packages/MySQLdb/cursors.py", line 341, in _do_query
db.query(q)
File "/home/xxx/virtualenv/myproject/lib/python3.4/site-packages/MySQLdb/connections.py", line 280, in query
_mysql.connection.query(self, query)
_mysql_exceptions.ProgrammingError: (1064, "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'None, None) NOT NULL)' at line 1")
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/home/xxx/virtualenv/myproject/bin/django-admin", line 11, in <module>
sys.exit(execute_from_command_line())
File "/home/xxx/virtualenv/myproject/lib/python3.4/site-packages/django/core/management/__init__.py", line 338, in execute_from_command_line
utility.execute()
File "/home/xxx/virtualenv/myproject/lib/python3.4/site-packages/django/core/management/__init__.py", line 330, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "/home/xxx/virtualenv/myproject/lib/python3.4/site-packages/django/core/management/commands/test.py", line 30, in run_from_argv
super(Command, self).run_from_argv(argv)
File "/home/xxx/virtualenv/myproject/lib/python3.4/site-packages/django/core/management/base.py", line 390, in run_from_argv
self.execute(*args, **cmd_options)
File "/home/xxx/virtualenv/myproject/lib/python3.4/site-packages/django/core/management/commands/test.py", line 74, in execute
super(Command, self).execute(*args, **options)
File "/home/xxx/virtualenv/myproject/lib/python3.4/site-packages/django/core/management/base.py", line 441, in execute
output = self.handle(*args, **options)
File "/home/xxx/virtualenv/myproject/lib/python3.4/site-packages/django/core/management/commands/test.py", line 90, in handle
failures = test_runner.run_tests(test_labels)
File "/home/xxx/virtualenv/myproject/lib/python3.4/site-packages/django/test/runner.py", line 210, in run_tests
old_config = self.setup_databases()
File "/home/xxx/virtualenv/myproject/lib/python3.4/site-packages/django/test/runner.py", line 166, in setup_databases
**kwargs
File "/home/xxx/virtualenv/myproject/lib/python3.4/site-packages/django/test/runner.py", line 370, in setup_databases
serialize=connection.settings_dict.get("TEST", {}).get("SERIALIZE", True),
File "/home/xxx/virtualenv/myproject/lib/python3.4/site-packages/django/db/backends/base/creation.py", line 368, in create_test_db
test_flush=True,
File "/home/xxx/virtualenv/myproject/lib/python3.4/site-packages/django/core/management/__init__.py", line 120, in call_command
return command.execute(*args, **defaults)
File "/home/xxx/virtualenv/myproject/lib/python3.4/site-packages/django/core/management/base.py", line 441, in execute
output = self.handle(*args, **options)
File "/home/xxx/virtualenv/myproject/lib/python3.4/site-packages/django/core/management/commands/migrate.py", line 179, in handle
created_models = self.sync_apps(connection, executor.loader.unmigrated_apps)
File "/home/xxx/virtualenv/myproject/lib/python3.4/site-packages/django/core/management/commands/migrate.py", line 309, in sync_apps
editor.create_model(model)
File "/home/xxx/virtualenv/myproject/lib/python3.4/site-packages/django/db/backends/base/schema.py", line 282, in create_model
self.execute(sql, params or None)
File "/home/xxx/virtualenv/myproject/lib/python3.4/site-packages/django/db/backends/base/schema.py", line 107, in execute
cursor.execute(sql, params)
File "/home/xxx/virtualenv/myproject/lib/python3.4/site-packages/django/db/backends/utils.py", line 64, in execute
return self.cursor.execute(sql, params)
File "/home/xxx/virtualenv/myproject/lib/python3.4/site-packages/django/db/utils.py", line 97, in __exit__
six.reraise(dj_exc_type, dj_exc_value, traceback)
File "/home/xxx/virtualenv/myproject/lib/python3.4/site-packages/django/utils/six.py", line 658, in reraise
raise value.with_traceback(tb)
File "/home/xxx/virtualenv/myproject/lib/python3.4/site-packages/django/db/backends/utils.py", line 62, in execute
return self.cursor.execute(sql)
File "/home/xxx/virtualenv/myproject/lib/python3.4/site-packages/django/db/backends/mysql/base.py", line 124, in execute
return self.cursor.execute(query, args)
File "/home/xxx/virtualenv/myproject/lib/python3.4/site-packages/MySQLdb/cursors.py", line 226, in execute
self.errorhandler(self, exc, value)
File "/home/xxx/virtualenv/myproject/lib/python3.4/site-packages/MySQLdb/connections.py", line 36, in defaulterrorhandler
raise errorvalue
File "/home/xxx/virtualenv/myproject/lib/python3.4/site-packages/MySQLdb/cursors.py", line 217, in execute
res = self._query(query)
File "/home/xxx/virtualenv/myproject/lib/python3.4/site-packages/MySQLdb/cursors.py", line 378, in _query
rowcount = self._do_query(q)
File "/home/xxx/virtualenv/myproject/lib/python3.4/site-packages/MySQLdb/cursors.py", line 341, in _do_query
db.query(q)
File "/home/xxx/virtualenv/myproject/lib/python3.4/site-packages/MySQLdb/connections.py", line 280, in query
_mysql.connection.query(self, query)
django.db.utils.ProgrammingError: (1064, "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'None, None) NOT NULL)' at line 1")
Core models file
# -*- coding: UTF-8 -*-
# /project_root/project_base/core/models.py
"""
Abstract models and common mixins for Django models.
"""
from __future__ import absolute_import
from __future__ import unicode_literals
from django.db import models
from django.utils.safestring import mark_safe
from django.utils.html import format_html
from django.template.defaultfilters import (
slugify,
filesizeformat,
)
from django.core.validators import (
MinLengthValidator,
MaxLengthValidator,
)
from utils.timespans import seconds_to_string
from utils.markup import parse_markdown
MARKDOWN_CONTENT_HELP = (
"…"
)
MARKDOWN_CONTENT_HELP_SHORT = (
"Inhalt des Artikels oder der Seite. Hier kann sowohl HTML direkt genutzt werden (z.B. um Videos einzubinden), als auch Markdown (<a href=\" http://ift.tt/22Qc9rz\">Spickzettel</a>)."
)
class TrimStringsMixin(object):
"""
Mixin that hooks into :meth:`clean_fields` to strip whithespace from
the content of text fields before cleaning them.
"""
def strip_whitespace(self):
"""
Strips whithespace from text-fields.
"""
for field in self._meta.fields:
value = getattr(self, field.name)
if value:
try:
setattr(self, field.name, value.strip())
except AttributeError:
pass
def clean_fields(self, *args, **kwargs):
"""
Calls :meth:`strip_whitespace` and then `super`.
"""
self.strip_whitespace()
super(TrimStringsMixin, self).clean_fields(*args, **kwargs)
class PlaytimeMixin(object):
"""
Adds a :attr:`playtime` property that fromats the playtime using
:func:`seconds_to_string`.
For the property a :attr:`seconds` attribute needs to be present on the
model, as field or trough the query set.
"""
def get_playtime(self):
"""
Returns the :attr:`seconds` as ``h:mm:ss`` string.
Or ``?`` if :attr:`seconds` is missing.
"""
try:
seconds = self.seconds if self.seconds else 0
return seconds_to_string(seconds, force_minutes=True)
except AttributeError:
return '?'
playtime = property(get_playtime)
class OrderedListMixin(object):
"""
Mixin with some methods to automatically arrange ordered items.
Saved objects are put at the end of the list if no *position* is set.
.. note::
"End of the list" means to the highest *position* + 1.
"Start of the list" means to the lowest *position* - 1.
.. important::
Models need to have a **position** attribute, e.g. inherit from
:cls:`OrderedObject`.
Ordered
-------
Per default, the standard manager ``objects`` is used to get the
objects on which :meth:`last()` and :meth:`first()` operate.
In most cases those objects are sorted by ``position`` because of the meta
settings. You can implement :meth:`get_ordered_objects` in subclasses to
return a :cls:`django.db.models.QuerySet` that is used instead.
Ordered Group
~~~~~~~~~~~~~
So you can have more that one **ordered group** in a model, e.g. journal
enties sorted by position but based on a FK to a journal…
"""
def save(self, move_to_end=True, *args, **kwargs):
"""
Sets *position* to end if not set.
"""
if move_to_end and not self.position:
self.move_to_end(save=False)
super(OrderedListMixin, self).save(*args, **kwargs)
def get_ordered_objects(self, manager):
"""
Returns a :cls:`django.db.models.QuerySet`.
Uses all relevant objects (e.g. all objects in the same **ordered group**
as *self*) in a given order.
The supplied *manager* should be the default manager for the class.
"""
raise NotImplementedError
def get_objects(self):
"""
Returns a :cls:`django.db.models.QuerySet` that contains all
:cls:`OrderedObject` objects that belong to the same **ordered group**
as *self*.
If no **ordered group** is returned by :meth:`get_ordered_objects`, then
all :cls:`OrderedObject` are used (as supplied by the default manager
``objects``).
"""
manager = self.__class__.objects
try:
return self.get_ordered_objects(manager).all()
except NotImplementedError:
return manager.all()
def move_to_end(self, save=True):
"""
Sets the *position* to the end of the list.
.. note::
*position* is set to `0` if there are no other instances.
"""
objects = self.get_objects()
last = objects.last()
if last is None:
self.position = 0
elif last.pk == self.pk:
return
else:
self.position = last.position + 1
if save:
self.save(move_to_end=False)
def move_to_start(self, save=True):
"""
Sets the *position* to the start of the list.
.. note::
*position* is set to `0` if there are no other instances.
.. important::
**Sideffect**: Even if you don't call :meth:`save`, the *position* of
all other objects might be increased by one.
"""
objects = self.get_objects()
first = objects.first()
if first is None:
self.position = 0
elif first.pk == self.pk:
return
else:
self.position = first.position - 1
if self.position < 0:
for obj in objects.order_by('-position'):
obj.position += 1
obj.save()
self.position = 0
if save:
self.save(move_to_end=False)
def get_number(self):
"""
Returns `position + 1` (so that `0` becomes `1`).
"""
return self.position + 1
number = property(get_number)
class ImageMixin(object):
"""
Some common properties for models that include an image field.
.. important::
Models need to have a **image** attribute.
"""
def get_name(self):
return self.image.name
get_name.short_description = 'Datei Name'
get_name.admin_order_field = 'image__name'
def get_url(self):
return self.image.url
get_url.short_description = 'URL'
get_url.admin_order_field = 'image__url'
def get_link(self):
return '<a href="{}">{}</a>'.format(self.url, self.name)
get_link.short_description = 'URL'
get_link.admin_order_field = 'image__url'
get_link.allow_tags = True
def get_size(self):
return filesizeformat(self.image.size)
get_size.short_description = 'Größe'
get_size.admin_order_field = 'image__size'
name = property(get_name)
url = property(get_url)
link = property(get_link)
size = property(get_size)
class ShortDescriptionMixin(object):
"""
Adds a `short_description` property.
.. important::
Models need to have a **description** attribute.
"""
def get_short_description(self, max_length=60):
"""
Returns the (shorted) *description*.
"""
if len(self.description) > max_length:
return self.description[:max_length] + '…'
else:
return self.description
get_short_description.short_description = 'Kurzbeschreibung'
get_short_description.admin_order_field = 'description'
short_description = property(get_short_description)
class OrderedObject(models.Model):
"""
Abstract model that adds a *position* field.
"""
class Meta:
abstract = True
ordering = ['position']
get_latest_by = 'position'
position = models.PositiveSmallIntegerField(
default=0,
help_text="Position in der Reihenfolge."
)
class TitleSlug(models.Model):
"""
Abstract model that adds a *title* and a *slug* field.
The *slug* is automatically updated on :meth:`save` calls.
"""
class Meta:
abstract = True
title = models.CharField(
'Titel', max_length=150,
validators=[MinLengthValidator(4), MaxLengthValidator(150)],
help_text="Ein beliebiger Title für den Eintrag."
)
slug = models.SlugField(max_length=150)
def save(self, *args, **kwargs):
self.slug = slugify(self.title)
super(TitleSlug, self).save(*args, **kwargs)
def get_short_title(self, max_length=60):
"""
Returns the (shorted) *title*.
"""
if len(self.title) > max_length:
return self.title[:max_length] + '…'
else:
return self.title
get_short_title.short_description = 'Title'
get_short_title.admin_order_field = 'title'
short_title = property(get_short_title)
class MarkdownContent(models.Model):
"""
Abstract model that adds a *content* and a *html* field.
The *content* field supports Markdown: the *html* field is automatically
updated on :meth:`save` calls to contain the HTML version of *content*.
"""
class Meta:
abstract = True
content = models.TextField(
'Inhalt', max_length=35000,
validators=[MinLengthValidator(6), MaxLengthValidator(35000)],
help_text=MARKDOWN_CONTENT_HELP
)
html = models.TextField(max_length=40000)
def save(self, *args, **kwargs):
self.html = parse_markdown(self.content)
super(MarkdownContent, self).save(*args, **kwargs)
def get_short_content(self, max_length=80):
"""
Returns the (shorted) *content*.
"""
if len(self.content) > max_length:
return self.content[:max_length] + '…'
else:
return self.content
get_short_content.short_description = 'Inhalt'
get_short_content.admin_order_field = 'content'
def get_html_safe(self):
"""
Returns the :attr:`html` attribute as safe string.
"""
return mark_safe(self.html)
def get_preview(self):
"""
Returns HTML "marked save for preview" for the Markdown in *content*.
The HTML is contained in a DIV with the id `preview`.
"""
preview = (
'<div id="md-preview">'
'<header>'
'<h1>{title}</h1>'
'</header>'
'{content}'
'</div>'
)
return format_html(
preview, title=self.title, content=self.get_html_safe()
)
get_preview.short_description = 'Preview'
get_preview.admin_order_field = 'content'
short_content = property(get_short_content)
preview = property(get_preview)
Test file
# -*- coding: UTF-8 -*-
# /project_root/project_base/core/test.py
"""
Test models for *core*.
"""
from __future__ import absolute_import
from __future__ import unicode_literals
from django.db import models
from .managers import (
FeatureManager
)
from .models import (
TrimStringsMixin,
PlaytimeMixin,
OrderedListMixin,
OrderedObject,
TitleSlug,
MarkdownContent,
)
class TrimStringsMixinTestModel(TrimStringsMixin, models.Model):
"""
Test–Model for the `TrimStringsMixin` mixin.
"""
title = models.CharField(max_length=50)
description = models.TextField()
class PlaytimeMixinTestModel(PlaytimeMixin, models.Model):
"""
Test–Model for the `PlaytimeMixin` mixin.
"""
title = models.CharField(max_length=50)
seconds = models.DecimalField()
class OrderedObjectTestModel(OrderedObject):
"""
Test–Model for the `OrderedObject` abstract class.
"""
title = models.CharField(max_length=50)
class OrderedGroupTestModel(OrderedListMixin, OrderedObject):
"""
Test–Model for the `OrderedObject` abstract class — with `OrderedListMixin`.
"""
class Meta(OrderedObject.Meta):
unique_together = ('position', 'gidx')
title = models.CharField(max_length=50)
gidx = models.IntegerField()
def get_ordered_objects(self, manager):
"""
Returns a :cls:`django.db.models.QuerySet` for the objects in the same
**ordered group** as *self*.
The supplied *manager* should be the default manager for the class.
"""
return manager.filter(gidx=self.gidx)
class FeatureTestModel(models.Model):
"""
Test–Model for the `FeatureManager` manager class.
"""
title = models.CharField(max_length=50)
featured = models.BooleanField(
default=False,
help_text="Soll dieses Objekt angezeigt werden?"
)
objects = FeatureManager()
class StripTestModel(models.Model):
"""
Test–Model for the `FormTrimStringsMixin` form class.
"""
title = models.CharField(max_length=12)
extra = models.CharField(max_length=12, blank=True)
class TitleSlugTestModel(TitleSlug):
"""
Test–Model for the `TitleSlug` abstract class.
"""
pass
class MarkdownContentTestModel(MarkdownContent):
"""
Test–Model for the `MarkdownContent` abstract class.
"""
pass
Aucun commentaire:
Enregistrer un commentaire