Change History (44)

What options are you using for the openid_key field?, is it a CharField? a ForeignKey?. Please post [a fragment with relevant parts to reproduce the problem of] your models.py file to the django-users mailing list.

Hm. The problem here is that fields which -- in Python -- reduce to a string value have null input stored as empty strings instead of as NULL (this is mentioned in ​the model docs), and with good reason; as the docs point out, allowing both NULL and an empty string creates ambiguity. I'm not sure what the best solution is here in the face of a nullable but unique field which holds a string.

Being that this is supposed to be on top of a DB layer I think a string should be treated as NULL when it's blank. At least if the field is set to null=True. In reality a string being is the same as being empty. I have no problems with this except for the admin interface.

We are not going to interpret empty strings as NULL. They are not the same thing, in general.

I think this is something that needs to be taken care of in the model's save() method or as the data is cleaned (converted from form to Python objects). Not closing yet, because we can probably come up with some documentation and best practices here, but we aren't going to start automatically storing NULLs.

This causes the admin interface to throw database constraint errors whenever there is more than one empty mac address in the table. If the admin interface correctly left these as NULL then everything would work properly. If you can tell me a better way to get the behaviour I need then I am open to suggestions (Field should be blank or unique) as I use this for a large number of fields and the fact that it works properly on everything except strings is very frustrating.

This seems more of an admin problem than a db wrapper problem. The db handles it fine (as far as I know) if you actually set the value to be None, it is just the fact that the admin can't tell (or accurately display) the difference between a blank string and null.

I think newforms-admin should have a special NullCharField widget for db CharFields which have null=True with two radio options, the first with a label of "None" and the second with the text box. This way, you can make the differentiation between a null and blank string.

The admin did indeed give a duplicate key error when trying to add a blank value twice. With a default of None (Null), I think the admin should submit None for a blank field, and therefore no key constraint should hit (since None != None or Null != Null). In the case where default is not set or is not None then I think an empty string is the correct value.

I think this is more appropriately placed in the Forms component, as it can be a problem with any form, not just those in the admin. At the heart of the issue is a mismatch between the option combinations available to form and model fields, and to solve it, form fields would need to understand the difference between submitting an empty value and no value, which HTTP form encoding does not make straight-forward without adding additional flags.

Given that, I am not sure this is something django *should* be dealing with, if it weren't for its own use of forms in the admin, so any solution should be 1) easy to use with any form, 2) but not required nor adding complexity to their existing behavior and usage, and 3) not part of the admin.

I like the idea of providing an OptionalWidget which can wrap other widgets, render them a checkbox to control if they are actually included, and easiyl used in forms without changing any existing usage or behavior.

I have created a NullableWidget, which accepts as its first argument another Widget. It renders a checkbox and its subwidget alongside and can correctly decompress and get value from datadict. Also wrote some tests. What is still needed:

I update zefciu patch with redefined _has_changed method and now save not throw exceptions anymore. But new problem is that CharField convert None value to empty string (u"") and save of another model instance with CharField set to None throws unique error again. So the root of the evil is in null and blank attributes again, not in unique itself?

Maybe a new form field init argument is needed: blank_as_none? Better name welcome. I don't see any other way forward. Currently it is impossible for the form layer to know if the given empty value should be treated as "" or None.

Maybe we need a matching model field init argument, too. Setting null=True, blank=True can not be translated to blank_as_none, as this would be backwards incompatible.

EDIT: I do see other ways forward, like the NullableWidget presented here...

Just to throw in my 2 cents here: if this is a general form problem the answer is not a special widget with a 'NULL' description as users of a site should not be expected to know the difference, which means one should be able to describe in the field's init method what the description of the radio buttons should be, like (..., provided_value="My cat's name", absent_value="I have no cat").

Also the simplest option is to always insert as NULL if null and unique are True as there are very few real world cases where an empty value is an actual valid option. One could also make this case for non-unique but null fields, however there's no actual distinction between a NULL value and an empty string in this case.

And there may be another solution. Backwards compatibility may be an issue but I think solvable. Instead of the blank field being a boolean, make it an integer flag:

blank = models.RESTRICT acts as blank=False now

blank = models.SET_EMPTY acts as blank=True now and sets field to the 'empty' value

I agree with msopacua's solution, and suggest to change component from Forms to contrib.admin because programmers should write their own form code to control it. The 'blank' parameter may only be used for admin app.

msopacua's suggestion is incomplete because it only considers the model-field API. Form fields don't even have a "blank" argument (they have "required"), so a different API would have to be proposed for them. Ultimately this is a form-field issue (we only need the option at model-field level to give a hint to modelform generation).

The NullableWidget is a bad general solution for the reason msopacua notes: in most cases end users don't know and shouldn't have to care about the difference between storing null and empty string; this is a concern for the application developer.

At the form field level, I think perhaps the best approach is similar to what akaariai suggested; a new default_if_empty option to forms.CharField, defaulting to the empty string, that defines what is returned from to_python if value in self.empty_values.

At the model-field level, my objection to msopacua's suggestion is that it implicitly applies to all field types (since they all have the blank option), but I think this issue is only relevant for CharField. If there were no backwards-compatibility concerns, I think the right behavior would be to simply automatically pass None to the form field's default_if_empty if null=True. I'm somewhat tempted to just go ahead and do this and note it in the backwards-compat notes, because I think in most cases if null=True it is the desired behavior. If that's too much of a back-compat break, I guess we would need a new option to CharField to enable that behavior.

msopacua's suggestion is incomplete because it only considers the model-field API. Form fields don't even have a "blank" argument (they have "required"), so a different API would have to be proposed for them. Ultimately this is a form-field issue (we only need the option at model-field level to give a hint to modelform generation).

I still think this is a model issue. For one, because the issue exists in translating model data to storage. Secondly, because I can think of a case, where the form would not present an empty string but the model might - like a bad word filter. For me the logical place to put such a thing would be in a model's clean(), but a case can be made to do this at the form level.

At the model-field level, my objection to msopacua's suggestion is that it implicitly applies to all field types (since they all have the blank option), but I think this issue is only relevant for CharField.

True. I don't mind this being a charfield only option, if it gets picked up by custom fields that derive from it. You'd name it differently and only apply it to CharField.lll

Can you elaborate? Simply adding that to the model save() method alone seems to be insufficient because commonly we are using a ModelForm and that calls the instance's validate_unique method, which raises ValidationError before the save method is reached...

You probably have one entry that has an empty string instead of null. Change that to null/none and that should fix the uniqueness problem. If you use my code in your save method, you just need to call save on that object.