Python 3.9, Django 3.2, Database is PostgreSQL hosted on ElephantSQL.
I have a model with a slug field which I have set to unique:
class website_category(models.Model):
fld1 = models.CharField(primary_key=True, max_length=8)
fld2 = models.TextField()
fld3 = models.SlugField(unique=True, db_index=True, max_length=100)
I can create new records for this model without any issue. However, when I try to edit an already existing record via the Django admin interface (e.g., change the text field fld2), Django throws this error:
website_category with this fld3 already exists
I can delete said record and re-enter the modified one without any issues, and I can edit the record if I change the slug field but not otherwise.
My guess is this is happening due to the "unique=True" parameter set in the slug field (fld3). However, I do want the slugs to be unique.
Is this an expected behavior of Django, or can I do something to make it possible for me to edit the records directly without having to delete and recreate them?
====
Edit: Additional Info
the model does not have any custom save method or ModelAdmin class. It is registered simply via admin.site.register(). The model does have a meta class which is being used to define some DB level constraints:
class Meta:
constraints = [
models.CheckConstraint(check=models.Q(fld1__iregex = r'^\w{8}$'),
name="%(app_label)s_%(class)s_id_validator"),
models.CheckConstraint(check=models.Q(fld3__iregex = r'^[\w-]+$'),
name="%(app_label)s_%(class)s_slug_validator"),
# DB level condition to enforce slug format (word characters and '-')
]
Related
When I define a non nullable field in django it allows me to save a model instance without specifying any value for this non-nullable field. This is not what I would expect. How can I force this to yield an error?
Postgres 9.1
django 2.1
windows
python 3.6
from django.db import models
class Wwtp(models.Model):
name = models.CharField(max_length=100, null=False,
blank=False, unique=True)
short_name = models.CharField(
max_length=10, null=False, blank=False, unique=True)
As expected, I am not allowed to save it with an explicit empty short_name.
mdl.Wwtp.objects.create(name='Wwtp4', short_name=None)
But I am allowed to save an instance of Wwtp without specifying short_name:
mdl.Wwtp.objects.create(name='Wwtp4')
and when I try:
mdl.Wwtp.objects.create()
it gives me
django.db.utils.IntegrityError: duplicate key value violates unique constraint "api_wwtp_short_name_key"
DETAIL: Key (short_name)=() already exists.
Apparently django filled the database with an empty value for short_name while it is not allowed to do so... How can I force the database to not allow this?
You can't with CharField. The empty value is an empty string '', not NULL. You already have blank=False set, so if you clean your model or model forms before saving them, you'll catch that. But it cannot be enforced at the database level.
Note that blank=False, null=False is the default, so you really don't have to specify that.
Also, if you really only want to support PostgreSQL, you could make a custom migration using RunSQL to create your column on the table, manually adding the SQL needed to add the constraint (e.g. using CHECK). See here for how to ensure Django also knows the column was created and doesn't try to add it in the next migration. There's an example here.
[Edit] In Django 2.2, you can add a CheckConstraint in the model's Meta class constraints attribute:
from django.db.models import CheckConstraint, Q
(...)
class Meta:
constraints = [
CheckConstraint(
check=~Q(name=''),
name='name_not_empty'),
CheckConstraint(
check=~Q(short_name=''),
name='short_name_not_empty']
I am using Django-Jet and have a model with many ForeignKey fields. For those fields I want their values retrieved dynamically via AJAX and not preloaded. One of the field is like this:
class Person(Base_Entity):
first_name = models.ForeignKey(
'Name',
null = True,
blank = True,
default = None,
verbose_name = _('first name of person'),
on_delete = models.SET_NULL,
related_name = 'is_first_name_of_%(app_label)s_%(class)s',
)
)
#staticmethod
def autocomplete_search_fields():
return 'first_name__name',
(The Name model has hundreds of entries, and there will be even more later)
It seems I cannot set that field to NULL in Django Admin (no line with dashes appears):
If I turn on autocomplete (i.e. remove the autocomplete_search_fields method), I do get that NULL entry, BUT I also get all the possible values preloaded in the HTML select, and that slows down the page loading to a point it is not usable.
I am using Django 2.1.4, Django-Jet 1.0.8 (I suspect the issue is closely related to Django-Jet)
Any help is appreciated.
I am using djangojet and this relation shows an empty value choice in admin ("-----"):
someModel_FK= models.ForeignKey(someModel,
related_name='this-model',
null=True,
blank=True,
on_delete=models.SET_NULL)
a - Remove django-jet and check on default admin.
b- Are you missing migrations ? is this "null=True,blank=True, " migrated to DB ?
Django 1.11.4 python 3.6
I have a default Django framework form I use for both update and create records for a given model. The primary key ("id" field) is generated by Django. The rest is defined in my model (see below). The model is subclassed from AuditModel class which overloads save method.
Everything works at this point, i.e. I can create new records or update existing records using standard Django forms interface.
class Product(AuditModel):
internal_id = models.CharField(max_length=100, null=True, blank=True, help_text="Internal ID")
external_id = models.CharField(max_length=100, null=False, blank=False, help_text="External ID", verbose_name="External ID")
label = models.ForeignKey(Label, help_text="Label")
class AuditModel(models.Model):
created = models.DateTimeField(null=True,editable=False)
updated = models.DateTimeField(null=True,editable=False)
def save(self, *args, **kwargs):
date = timezone.localtime()
if self._state.adding :
self.created = date
self.updated = date
super(AuditModel, self).save()
My question: I would like external_id to be unique (but not a primary key), i.e.
external_id = models.CharField(max_length=100, unique=True, null=False, blank=False, help_text="External ID", verbose_name="External ID")
Once I added unique=True to the definition of the external_id field, the behaviour of the view changes: on attempt to update existing record I get an error message next to external_id textbox "Product with this External ID already exists." and no change happens in the DB. Somehow presence of unique=True in the definition of the external_id field make Django to think that I am not editing an existing record but arrived to this form to create a new entry.
The url I arrived to the screen with is correct, i.e /product/<some id here>/change/, not /product/add
In the DB all the existing values in external_id field are non-null (no empty strings either) and unique.
If I understood correctly by adding some debug, the error "Product with this External ID already exists" happens BEFORE save() is even called, like unique=True invokes some Django data validator that happened to be unaware of the current action (update vs insert) and the view is just reloaded with an error.
Solved - The problem was caused by commented out line # instance._state.adding = False inside def from_db() method of a superclass. No idea why it got commented out.
As can be seen below, I have 2 models connected via an intermediary model to form a ManyToMany relationship. The problem is that, when I delete a Tender object in get this error.
update or delete on table "tender_details_tender" violates foreign key constraint "user_account_company_tender_id_984ea78c_fk_tender_de" on table "user_account_companyprofile_assignedTenders"
DETAIL: Key (id)=(1) is still referenced from table "user_account_companyprofile_assignedTenders".
I thought by adding on_delete=models.CASCADE in the ForeighKeys (i.e. in the intermediary model) would solve this problem, but apparently not.
class CompanyProfile(models.Model):
assignedTenders = models.ManyToManyField(Tender, through='UsersTenders', related_name='UserCompanies')
# connects users to the tenders they match.
class UsersTenders(models.Model):
user = models.ForeignKey(CompanyProfile, on_delete=models.CASCADE, related_name='userTenderLink')
tender = models.ForeignKey(Tender, on_delete=models.CASCADE, related_name='userTenderLink')
sent = models.BooleanField(default=False, blank=False)
class Meta:
unique_together = ("user", "tender")
class Tender(models.Model):
tenderCategory = models.ManyToManyField(Category, blank=False) #this field holds the tender category, e.g. construction, engineering, human resources etc.
tenderProvince = models.ManyToManyField(Province, blank=False) #this is the province the tender was a
For what its worth, I know what is causing this problem, what I don't know is how to fix it. The problem is that initially I had the ManyToManyField under the CompanyProfile model without the "through" argument, so as you might imagine Django created it's own intermediary table which is "user_account_companyprofile_assignedTenders" as shown in the error. I later decided to create my own intermediary model (i.e. UsersTenders) because I wanted an extra field there, so I had to add the "through" argument in my ManyToManyField (i.e. 'assignedTenders'). That worked fine but the old intermediary model "user_account_companyprofile_assignedTenders" did not get deleted automatically, I assume its because a few relationship had be created before the change. How can I delete "user_account_companyprofile_assignedTenders" without destabilizing my project.
Did you add on_delete after database migration? If so have you made a migration after adding on_delete?
You could try to set null=True to all fields and then try ti figure out which foreign key is causing problems.
bdw. when you set blank=True that only means that your form fields will not insist on this fields to be filled for submitting.
I have a Post models for blog:
сlass Post(models.Model):
author = models.ForeignKey(User,
related_name="blog_posts",
on_delete=models.CASCADE)
........
........
And I want to add a Tag to the project, so I made a Tag model:
class Tag(models.Model):
tag_name = models.CharField(max_length=20,
blank=True)
def __str__(self):
return self.tag_name
I added this to the Post model:
tag = models.ForeignKey(Tag, related_name="blog_tag",
on_delete=models.CASCADE)
But makemigration give to me an error:
You are trying to add a non-nullable field 'tag' to post without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
2) Quit, and let me add a default in models.py
So why i have to populate existing rows? And how i can to live the rows empty?
P.S. If i chose 1) i still have an error.
You already have data in your db and now you are adding a new column tag in your database table, which is also not null.
So django is saying that either you provide one-default data for the previously existing rows or you make this new column (tag) a null value (by adding null=True in arguments of the field) so that already existing rows will fill it with null.