object created even if field was required - python

#models.py
class Mymodel(models.Model):
name = models.CharField(max_length=100,null=False,blank=False)
email = models.EmailField(max_length=100,null=False,blank=False)
password = models.CharField(max_length=120,null=False,blank=False)
email_notification = models.BooleanField()
#views.py
obj=MyModel.objects.create(name="ok",password="dsfdsfdsfdsfsfds",email_notification=1)
even if email was required field,then also object was created when I see in the admin panel.What can be the issue,Why object got created,even if email field was mandatory?
Also if I go in admin panel and open that object and click save then it raises that email is required

Note: You don't to have provide null=False,blank=False in your fields because those are the values used by default.(See the Django Field __int__ signature.).
def __init__(self, verbose_name=None, name=None, primary_key=False,
max_length=None, unique=False, blank=False, null=False,
db_index=False, rel=None, default=NOT_PROVIDED, editable=True,
serialize=True, unique_for_date=None, unique_for_month=None,
unique_for_year=None, choices=None, help_text='', db_column=None,
db_tablespace=None, auto_created=False, validators=(),
error_messages=None):
By default all the fields in database are created with NOT NULL constraint. If you set null=True for a particular field, then django sets NULL on the column in your DB. It’s the database equivalent of Python’s None keyword.
Example with null argument
Assume that I have the following Mymodel in my my_app and I set email field to null=True.
class MyModel(models.Model):
name = models.CharField(max_length=100)
email = models.EmailField(max_length=100, null=True)
password = models.CharField(max_length=120)
email_notification = models.BooleanField()
In Shell,
>> from my_app.models import MyModel
>> new = MyModel.objects.create(name="ok",
password="dsfdsfdsfdsfsfds",
email_notification=1)
>> new.email == None
>> True # As you can see Django sets db value
# as NULL and when we query the data it converts back to Python `None` object.
Example without null argument
Assume that I have the following Mymodel in my my_app.(remember null will be False by default)
class MyModel(models.Model):
name = models.CharField(max_length=100)
email = models.EmailField(max_length=100)
password = models.CharField(max_length=120)
email_notification = models.BooleanField()
In Shell,
>> from my_app.models import MyModel
>> new_obj = MyModel.objects.create(name="test",
password="test",
email_notification=1)
>> new_obj.email == ''
>> True
Ie,Django CharField and TextField the default values are stored in the DB as an empty string (''). In other words, if you create an object without providing values for a CharField(or a TextField) under the hood Django invokes the get_default method and returns '' (only in this case). This value will be stored in the database.
The following is the source code of get_default method.
def get_default(self):
"""Return the default value for this field."""
return self._get_default()
#cached_property
def _get_default(self):
if self.has_default():
if callable(self.default):
return self.default
return lambda: self.default
if not self.empty_strings_allowed or self.null and not connection.features.interprets_empty_strings_as_nulls:
return return_None
return str # return empty string
Let's answer your question:
Why object got created,even if email field was mandatory?
The answer is EmailField is an instance of CharField Hence default value '' will be used while creating an object in database. That is why you are not getting django.db.utils.IntegrityError.
>> new_obj = Mymodel.objects.create(name='tes1t', password='test1', email_notification=1)
>>> new_obj.email
''
Also if I go to the admin panel and open that object and click save then
it raises an error indicating that email is required
Remember blank is different from null. null is purely database-related, whereas blank is validation-related. So when you create an object directly in Python code, or execute raw SQL yourself, you are actually bypassing all of Django’s input validation. But in admin, Django is validating the input through the model form. Since in your case blank is set to False(blank not allowed), model form will raise Email is required Error.
Here is the relevant Django documentation for blank argument.
Field.blank
If True, the field is allowed to be blank. Default is False. Note
that this is different than null. null is purely database-related,
whereas blank is validation-related. If a field has blank=True, form
validation will allow entry of an empty value. If a field has
blank=False, the field will be required.
Additional resources
Django tips: the difference between ‘blank’ and ‘null’
differentiate null=True, blank=True in django

There is no issue with django you will have to create proper Django Model Form validation so that the empty string isn't ignored and it will raise an error for the blank field.

Related

How to add a default array of values ​to ArrayField?

Is it possible to add a default value to ArrayField?
I tried to do this for email field, but this did not work:
constants.py:
ORDER_STATUS_CHANGED = 'order_status_changed'
NEW_SIGNAL = 'new_signal'
NOTIFICATION_SOURCE = (
(ORDER_STATUS_CHANGED, 'Order Status Changed'),
(NEW_SIGNAL, 'New Signal'),
)
models.py:
from notifications import constants
from django.contrib.postgres.fields import ArrayField
class NotificationSetting(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True, related_name='notification_setting')
telegram = ArrayField(models.CharField(
choices= constants.NOTIFICATION_SOURCE,
max_length=30
), default=list)
email = ArrayField(models.CharField(
choices= constants.NOTIFICATION_SOURCE,
max_length=16
), default=list(dict(constants.NOTIFICATION_SOURCE).keys()))
class Meta:
db_table = 'notification_settings'
def __str__(self):
return f'Notification setting for user {self.user}'
And override the save method of the model would be bad practice, I think.
The problem is that in the django admin site I see that the default values did not count when the object was created. (UPD. Maibe i have problem with my custom ChoiseArrayField widged)
And i get this mesagge:
WARNINGS:
notifications.NotificationSetting.email: (postgres.E003) ArrayField default should be a callable instead of an instance so that it's not shared between all field instances.
HINT: Use a callable instead, e.g., uselistinstead of[]``
The default property on an ArrayField should be a callable. You can read more about that here: https://docs.djangoproject.com/en/3.0/ref/contrib/postgres/fields/.
What you are getting by placing directly there list(dict(constants.NOTIFICATION_SOURCE).keys()) is just a warning so it should still add the defaults to the field. By placing this default directly there it will put in the migrations the following thing and the values will be shared across all field instances:
default=['order_status_changed', 'new_signal']
To get rid of the warning you should create a function that returns the default value:
def get_email_default():
return list(dict(constants.NOTIFICATION_SOURCE).keys())
and put the function as the default to the field:
email = ArrayField(models.CharField(
choices= constants.NOTIFICATION_SOURCE,
max_length=16
), default=get_email_default)
By doing this the warning will be gone and from the function you can have logic for choosing the default value.
After doing this, in the migrations the default value will look like this:
default=my_model.models.get_email_default

Django admin not showing choices for Foreign key field

I am creating a small chat app and I have problems making a message in the admin because of the drop down for the Messagemie model for field chat. Notice in the picture below it does not show the required values associated with the Conversation model. The values that the conversation field in the Conversation model accepts are of the form "number-number", e.g. 5-10, 11-21 etc. Note that I have created a mechanism not shown below which converts such input formats to strings for non Django admin input (When users start a new conversation).
The conversation field is of type CharField. I suspect the reason why Django admin form does not show the required values is because of the field type, however I am not sure. Also it could be that because Django admin is not converting the input to string thus showing just Conversation object in the drop down. Why is Django admin not showing the correct values for the chat input field?
#python_2_unicode_compatible
class Conversation(models.Model):
conversation = models.CharField(unique=True, max_length=150)
email_1 = models.ForeignKey(Usermie, to_field="email", related_name="email_1_convo")
email_2 = models.ForeignKey(Usermie, to_field="email", related_name="email_2_convo")
#python_2_unicode_compatible
class Messagemie(models.Model):
sender = models.ForeignKey(Usermie, to_field="email", related_name="email_sender")
receiver = models.ForeignKey(Usermie, to_field="email", related_name="email_receiver")
# The username is the sender's username
sender_username = models.CharField( max_length=50)
receiver_username = models.CharField(max_length=50)
message = models.TextField()
chat = models.ForeignKey(Conversation, to_field="conversation", related_name="conversation_chat")
Picture showing Messagemie model chatfield selection in admin
Picture of input values in Conversation model Django admin.
Django admin shows the string representation of the object in the dropdown. This could be obtained by calling str(object). You can modify this behaviour by overriding the __str__ method in your class.
The implementation of the Django base model class (django.db.models.Model) has an implementation like below (for python3) -
def __str__(self):
return str('%s object' % self.__class__.__name__)
which explains what you see. self.__class__.__name__ evaluates to "Conversation", hence you end up seeing "Conversation object" in the dropdown.
To change this behaviour you can override the __str__ method to get the desired value returned. One sample implementation is below. You could modify the method easily to do include any logic you want.
class Conversation(models.Model):
conversation = models.CharField(unique=True, max_length=150)
email_1 = models.ForeignKey(Usermie, to_field="email",
related_name="email_1_convo")
email_2 = models.ForeignKey(Usermie, to_field="email",
related_name="email_2_convo")
def __str__(self):
return self.conversation

In Django 1.8, what does "USERNAME_FIELD" mean by authentication system?

I have been learning Django for a week, In order to implement authentication system, I have created models.py file as tutorials.
from django.db import models
from django.contrib.auth.models import AbstractBaseUser
class User(AbstractBaseUser):
username = models.CharField('username', max_length = 10, unique = True, db_index = True)
email = models.EmailField('email address', unique = True)
joined = models.DateTimeField(auto_now_add = True)
is_active = models.BoolenField(default = True)
is_admin = models.BoolenField(default = False)
USERNAME_FIELD = 'username'
def __unicode__(self):
return self.username
I understand what username, email, joined, is_active, is_admin means, but I can't understand why I use USERNAME_FIELD.
Is username created by models.CharField equal to the 'username' in USERNAME_FIELD?
Why do I have to create USERNAME_FIELD?
What does def __unicode__(self): function mean?
According to the docs, USERNAME_FIELD is:
A string describing the name of the field on the user model that is used as the unique identifier. This will usually be a username of some kind, but it can also be an email address, or any other unique identifier. The field must be unique (i.e., have unique=True set in its definition), unless you use a custom authentication backend that can support non-unique usernames.
So, USERNAME_FIELD specifies which model field is going to be used as the username. If your application uses an email address instead of a username, you would configure that using USERNAME_FIELD.
The __unicode__(self) method returns a string representation of the object. Without it, any time you try to display an object it will look like: <User: User object>. As you have it now, displaying a User object will instead show the User's username. In the Django tutorial part 2 they use the __str__ method in conjunction with the #python_2_unicode_compatible decorator to make it work with Python 2. In Python 3 __str__ is the equivalent of __unicode__ in Python 2.
Check the documentation for your version of Django:
USERNAME_FIELD
A string describing the name of the field on the User model that is used as the unique identifier. This will usually be a username of
some kind, but it can also be an email address, or any other unique
identifier. The field must be unique (i.e., have unique=True set in
its definition).
USERNAME_FIELD defaults to "username" so you can skip setting it in your custom user model if the default works for you.
You can read about __str__() and __unicode__() methods here.

ModelForm won't validate because missing value, but model field has null=True

I have a problem where my ModelForm is trying to assign '' to a field (it saves fine if I actually provide the primary key of the Product, but it's not a compulsory field, and won't save if the field is left blank). I take it that the ORM it's trying to set that field to ''but:
Shouldn't '' be coerced to None, and;
Why isn't the model form trying to set that field to None in the first place instead of
''?
models.py
class Question(models.model):
fk_product = models.ForeignKey(Product, on_delete=models.SET_NULL, null=True, related_name="product_question")
forms.py
class QuestionForm(forms.ModelForm):
fk_product=forms.ChoiceField(required=False)
class Meta:
model = Question
fields = ['fk_product',]
The error:
Cannot assign "''": "Question.fk_product" must be a "Product"
instance.
The view code that produces the error:
QuestionModelFormset = modelformset_factory(Question,
form=QuestionForm,
extra=1)
question_formset = QuestionModelFormset(
data=request.POST,
files=request.FILES,
queryset=Question.objects.all())
if not question_formset.is_valid(): #error occurs on this line
Try adding blank=True too.
null=True means that this field is allowed to be NULL in database.
blank=True means it can be submitted without a value in forms. Otherwise it must have value.

Django unique, null and blank CharField giving 'already exists' error on Admin page

I've been getting the most weird error ever. I have a Person model
class Person(models.Model):
user = models.OneToOneField(User, primary_key=True)
facebook_id = models.CharField(max_length=225, unique=True, null=True, blank=True)
twitter_id = models.CharField(max_length=225, unique=True, null=True, blank=True)
suggested_person = models.BooleanField(default=False)
I recently added the twitter_id field. When I access the Django admin page, and try to change the 'person' into a suggested_person, I get the following error:
Person with this Twitter id already exists.
I find this error to be extremely strange because the Facebook_id field is designed the exact same way as the Twitter_id field.
What could be the reason for this?
None of the answers clearly describe the root of the problem.
Normally in the db you can make a field null=True, unique=True and it will work... because NULL != NULL. So each blank value is still considered unique.
But unfortunately for CharFields Django will save an empty string "" (because when you submit a form everything comes into Django as strings, and you may have really wanted to save an empty string "" - Django doesn't know if it should convert to None)
This basically means you shouldn't use CharField(unique=True, null=True, blank=True) in Django. As others have noted you probably have to give up the db-level unique constraint and do your own unique checks in the model.
For further reference, see here: https://code.djangoproject.com/ticket/4136
(unfortunately no good solution decided at time of writing)
NOTE:
As pointed out by #alasdair in a comment, that bug has now been fixed - since Django 1.11 you can use CharField(unique=True, null=True, blank=True) without having to manually convert blank values to None
This is an old one but I had a similar issue just now and though I would provide an alternative solution.
I am in a situation where I need to be able to have a CharField with null=True, blank=True and unique=True. If I submit an empty string in the admin panel it will not submit because the blank string is not unique.
To fix this, I override the 'clean' function in the ModelForm, and in there I check if it's a blank string and return the result accordinly.
class MyModelChangeForm(forms.ModelForm):
class Meta:
model = models.MyModel
fields = ['email', 'name', 'something_unique_or_null',]
def clean_something_unique_or_null(self):
if self.cleaned_data['something_unique_or_null'] == "":
return None
else:
return self.cleaned_data['something_unique_or_null']
This fixed the problem for me without having to sacrifice the unique attribute on the model field.
Hope this helps.
EDIT:
You need to change where I have put "something_unique_or_null" to the name of your field. For example "clean_twitter_id".
In Django 1.11, form CharFields will have an empty_value argument, which allows you to use None if the field is empty.
Model forms, including the Django admin, will automatically set empty_value=None if the model's CharField has null=True.
Therefore, you will be able to use null=True, blank=True and unique=True together in your model CharField without the unique constraint causing problems.
Since you have null=True, blank=True and unique=True, django is considering None or blank as a unique entry. Remove the unique constraint and handle the uniqueness part in the code.
It's important to solve this at the model level, not at the form level, since data can enter through APIs, through import scripts, from the shell, etc. The downside of setting null=True on a CharField is that the column could end up with both empty strings and NULLs, which is slightly ambiguous but not generally a problem in my experience. If you're willing to live with that ambiguity, here's how to do it in a few steps:
1) Set null=True, blank=True on the field and migrate in the change.
2) Massage your data so that all existing empty strings are changed to NULLs:
items = Foo.objects.all()
for item in items:
if not item.somefield:
item.somefield = None
item.save()
3) Add a custom save() method to your model:
def save(self, *args, **kwargs):
# Empty strings are not unique, but we can save multiple NULLs
if not self.somefield:
self.somefield = None
super().save(*args, **kwargs) # Python3-style super()
4) Set unique=True on the field and migrate that in as well.
Now you'll be able to store somefield as empty or as a unique value whether you're using the admin or any other data entry method.
If you prefer not to have several migrations, here's an example of how to do it in a single migration:
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
def set_nulls(apps, schema_editor):
Event = apps.get_model("events", "Event")
events = Event.objects.all()
for e in events:
if not e.wdid:
e.wdid = None
e.save()
class Migration(migrations.Migration):
dependencies = [
('events', '0008_something'),
]
operations = [
migrations.AlterField(
model_name='event',
name='wdid',
field=models.CharField(blank=True, max_length=32, null=True),
),
migrations.RunPython(set_nulls),
migrations.AlterField(
model_name='event',
name='wdid',
field=models.CharField(blank=True, max_length=32, null=True, unique=True),
),
]
The root of the problem is that Django persist the empty value as empty-string, and not as null. To fix this, you can subclass CharField as follows:
class CharNullField(models.CharField):
description = "CharField that stores NULL"
def get_db_prep_value(self, value, connection=None, prepared=False):
value = super(CharNullField, self).get_db_prep_value(value, connection, prepared)
if value=="":
return None
else:
return value
So get_db_prep_value will make it sure that null gets persisted.
You have to provide default=None in the field
facebook_id = models.CharField(max_length=225,unique=True, null=True,blank=True,default=None)
This worked for me . I used it in phone number. So it can be unique if entered and not mandatory.
you are accepting blank values and expect them to be unique. this means there can only be ONE entry with a blank twitter_id
you can
either remove the unique contraint
remove the blank =True
give a default value for the field ( but default needs to be unique)

Categories

Resources