how to handle an 'auto-now' field in a django form? - python

In one of my django models, I have a field like this:
modified = models.DateTimeField(auto_now=True)
I thought, when creating a ModelForm for this model, I could just skip this field, and Django would populate it automatically.
My ModelForm:
class FooForm(forms.ModelForm):
class Meta:
model = Foo
fields = ['text', 'name', 'description'] # notice - modified field not included -
# should not be shown to the user
But, even though it didn't show up in the form, when submitting, when creating a new object, I got an exception:
IntegrityError at /url/ - null value in column "modified" violates not-null constraint
How can I make this work?

The auto_now field is not populated automatically in such a case.
You need to use auto_add_now, so the field definition looks like this:
modified = models.DateTimeField(auto_now_add=True)
Then, django will add the date automatically if the field is not shown.

You just need to provide a default value to the field.
This can be done as follows:
from datetime import datetime
modified = models.DateTimeField(default=datetime.now)

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

Unable to create instance of Django model

I'm trying to create an instance of this Report model:
class Report(models.Model):
"""
A model for storing credit reports pulled from Equifax.
"""
user = models.ForeignKey(to=CustomUserModel, on_delete=models.CASCADE,
help_text='User report belongs to.')
timestamp = models.DateTimeField(default=timezone.now)
report = JSONField()
However, whenever I try I get this error:
Exception Type: TypeError at /internal/report
Exception Value: 'report' is an invalid keyword argument for this function
This happens whether I instantiate the instance using the Report().save() method, or the Report.object.create() method as follows:
report_obj = Report.objects.create(
user=user,
report=report
)
Does anyone have any clue what's going on? There is very clearly a "report" attribute for that class, so why the error?
Thanks!
Based on the the error and the comment:
(...) Looks like I imported the form field from DRF instead of the model field of the same name from Django (...)
You did not import a JSONField that is a model field, but something else (for example a form field, or here a DRF field). As a result, Django does not consider report to be a field of your Report module, it sees it as a "vanilla" Python attribute.
You thus should make sure that JSONField links to the model field class instead. Adding such field will probably result in another migration to add the field to the database table:
from django.contrib.postgres.fields import JSONField
class Report(models.Model):
"""
A model for storing credit reports pulled from Equifax.
"""
user = models.ForeignKey(to=CustomUserModel, on_delete=models.CASCADE,
help_text='User report belongs to.')
timestamp = models.DateTimeField(default=timezone.now)
report = JSONField()

Django (DRF) Serializer inserting NULL

I've got an issue with Django where I am attempting to insert a new row into a MySQL database without providing NULLs for unspecified data.
For example, consider the following:
class MyModel(models.Model):
class Meta:
db_table = 'model_table'
managed = False
a = models.AutoField(primary_key=True)
b = models.CharField(max_length=255)
c = models.CharField(max_length=255, blank=True)
When providing the following data to the Serializer for a save():
data = list()
data['b'] = 'Some Text'
serializer = serializers.MyModelSerializer(data=data)
serializer.save()
The resulting SQL generated has NULL for every unspecified field, in this case 'c'. The table in the database does not allow NULL but has default values in place to handle unspecified data.
Is it possible to override this Serializer functionality to allow fields to be entirely omitted when attempting to insert, or will I have to create a new model with those fields omitted and have a 'creation' model for this specific case that does not have any awareness of these fields?
Note: the Serializer only has the field 'b' in its fields variable, so it has no immediate awareness of the 'c' column.
Edit: to try and clarify a little, this is the SQL that is generated, which due to nulls not being allowed in the column, is failing. 'c' does have default values however.
INSERT INTO `model_table` (`b`, `c`) VALUES ('Some Text', NULL)
It's fine for the AutoField of 'a' the PK, this is omitted by default, but 'c' is replaced with a NULL due to no value being provided in the data provided to the serializer, instead of being omitted form the SQL entirely. As a workaround I've duplicated the model and removed the fields I don't need from it just to get it working, and the SQL generated is valid due to the model itself having no awareness of the columns, but this does not seem like the best way to do this. The following is what I want this model and serializer combination to output
INSERT INTO `model_table` (`b`) VALUES ('Some Text')
so that 'c' is provided the default column value provided by MySQL itself. The model and serializer attmepting to insert NULL when I have provided not provided any data to the serializer is the problem.
you can override serializer create method like following
class UserSerializer(serializers.ModelSerializer):
profile = ProfileSerializer()
class Meta:
model = User
fields = ('username', 'email', 'profile')
def create(self, validated_data):
profile_data = validated_data.pop('profile')
user = User.objects.create(**validated_data)
Profile.objects.create(user=user, **profile_data)
return user

How to pre-fill a modelform field DJango

ive been searching for a while for information on how to do this.
here is my situation.
i have a ContactForm(form.ModelForm):
and it excludes the ID field
which i want to be auto generated based on the last entry to the Contact table
but im not finding any examples on how to do this
the help im lookin for is how to enter information without being visible to the user while at the same time automatically pre-fill the right id and i cant use pk cause it has a foriegn key to another table
If you define a field of a model as an AutoField it will do this exact thing
EDIT:
If you want to define a custom auto incrementing ID you can do it like:
from django.db import models
class MyModel(models.Model):
_id_counter = 0
custom_id = models.IntegerField()
#classmethod
def create(cls):
my_model = cls(custom_id=MyModel._id_counter)
MyModel._id_counter += 1
return my_model
model1 = MyModel.create()

Django Admin TabularInline Complaining About Missing Field

I have the following model and TabularInline subclasses:
class SomeModel(models.Model):
name = models.CharField(max_length=50)
class SomeModelInline(admin.TabularInline):
model = SomeModel
class SomeOtherModelAdmin(admin.ModelAdmin):
inlines = [SomeModelInline]
Without explicitly specifying the TabularInline's fields, Django's admin shows the fields "id" and "name". However, when I try and do:
class SomeModelInline(admin.TabularInline):
model = SomeModel
fields ['id','name']
Django throws the ImproperlyConfigured exception:
'SomeModelInline.fields' refers to field 'id' that is missing from the form.
What's going on here? Why can't I explicitly specify the id, even though Django's clearly capable of accessing it?
Ids are non-editable, by default inline shows the editable fields, but you can show the non-editable fields as well
From django docs
fields can contain values defined in ModelAdmin.readonly_fields to be
displayed as read-only.
So first add 'id' to readonly_fields, then add it to fields

Categories

Resources