Opened 11 years ago
Closed 11 years ago
#21194 closed Bug (duplicate)
Foreign Keying To A Field With An Empty String Value Converts To Null Database Value
Reported by: | Owned by: | nobody | |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | dev |
Severity: | Normal | Keywords: | |
Cc: | bmispelon@… | Triage Stage: | Accepted |
Has patch: | yes | Needs documentation: | no |
Needs tests: | yes | Patch needs improvement: | yes |
Easy pickings: | no | UI/UX: | no |
Description
If I have these two models:
class A(models.Model): b = models.ForeignKey(B) class B(models.Model): asdf = models.CharField(max_length = 10, primary_key=True)
then
empty = B() empty.asdf = '' empty.save() alpha = A() alpha.b = empty alpha.save()
will break because django will try to convert the asdf_id field to NULL when creating the sql statement. (I'm running MySQL, but I'm guessing this affects non-mysql databases)
The traceback is
File "/usr/local/lib/python2.7/dist-packages/django/db/models/base.py", line 546, in save force_update=force_update, update_fields=update_fields) File "/usr/local/lib/python2.7/dist-packages/django/db/models/base.py", line 626, in save_base rows = manager.using(using).filter(pk=pk_val)._update(values) File "/usr/local/lib/python2.7/dist-packages/django/db/models/query.py", line 605, in _update return query.get_compiler(self.db).execute_sql(None) File "/usr/local/lib/python2.7/dist-packages/django/db/models/sql/compiler.py", line 1018, in execute_sql cursor = super(SQLUpdateCompiler, self).execute_sql(result_type) File "/usr/local/lib/python2.7/dist-packages/django/db/models/sql/compiler.py", line 840, in execute_sql cursor.execute(sql, params) File "/usr/local/lib/python2.7/dist-packages/django/db/backends/util.py", line 41, in execute return self.cursor.execute(sql, params) File "/usr/local/lib/python2.7/dist-packages/django/db/backends/mysql/base.py", line 120, in execute return self.cursor.execute(query, args) File "/usr/lib/python2.7/dist-packages/MySQLdb/cursors.py", line 176, in execute if not self._defer_warnings: self._warning_check() File "/usr/lib/python2.7/dist-packages/MySQLdb/cursors.py", line 92, in _warning_check warn(w[-1], self.Warning, 3) _mysql_exceptions.Warning: Column 'base_element_id' cannot be null
I've specifically found that the get_db_prep_save function in db/models/fields/related.py:1042 is changing the empty string value over to None, which then gets converted to NULL in sql. I also noticed there's an unused "empty_strings_allowed = False" in that function's class.
Change History (8)
comment:1 by , 11 years ago
comment:2 by , 11 years ago
Cc: | added |
---|---|
Has patch: | set |
Triage Stage: | Unreviewed → Accepted |
Version: | 1.5 → master |
Hi,
Thanks for the detailed report. I can indeed reproduce this under sqlite too.
I'll add that this affects master too, and doesn't appear to be a regression (the bug is present in older versions of django as well).
comment:3 by , 11 years ago
Needs tests: | set |
---|---|
Patch needs improvement: | set |
Regarding your patch, I'm not familiar enough with the ORM code to review it in depth but I did give it a quick look.
It does seem to fix the issue you're reporting and it passes the existing test suite (with sqlite).
However, it lacks tests of its own: see our contributor guide for more info [1].
Thanks.
[1] https://docs.djangoproject.com/en/1.5/internals/contributing/writing-code/submitting-patches/#patch-style
comment:4 by , 11 years ago
I'll add in tests soon then. I'm not entirely sure my code is correct either. I may need to check for TextField in addition to CharField, but I'm under the impression that you can't foreign key to a text field anyways (or that may be database specific).
comment:5 by , 11 years ago
I added a test for it in the latest commit in the pull request (and made a fix suggested in a pull request comment).
comment:6 by , 11 years ago
Hi,
Your updated pull request looks much better now: good job!
I've made a few "stylistic" comments but overall it's pretty good I think.
I'll try to get someone better-versed in the ORM internals to review this, just to make sure I'm not missing anything.
Thanks for your contribution.
comment:8 by , 11 years ago
Resolution: | → duplicate |
---|---|
Status: | new → closed |
I believe this is a duplicate of #19299. That ticket has a slightly different patch, so maybe you could take a look there and see which approach you think is correct.
I added a patch at https://github.com/django/django/pull/1690