What does this Django migration error message mean? - django

When I try to create migrations for the Django models shown below, I'm getting an error message I don't understand. I am trying to model a website member who can add one or more other members as either friends or followers. In addition, a member can block any other member. Here are my models:
class Member(models.Model):
FRIEND = "friend_of"
FOLLOWS = "follows"
RELATION_TYPES = ((FRIEND, "friend"), (FOLLOWS, "follower"))
user = models.OneToOneField(User, on_delete=models.CASCADE)
relations = models.ManyToManyField(
"self", choices=RELATION_TYPES, through="MemberRelation"
)
blocks = models.ManyToManyField("self", through="MemberBlock")
def __str__(self):
return self.user.first_name
class MemberRelation(models.Model):
source = models.ForeignKey(
"Member", related_name="source_member", on_delete=models.CASCADE
)
target = models.ForeignKey(
"Member", related_name="target_member", on_delete=models.CASCADE
)
relation = models.CharField(max_length=8) # Contains Member.FRIEND or .FOLLOWER
def __str__(self):
return "Member {} {} member {}".format(self.source, self.relation, self.target)
class MemberBlock(models.Model):
source = models.ForeignKey(
"Member", related_name="blocker", on_delete=models.CASCADE,
)
target = models.ForeignKey(
"Member", related_name="blocked", on_delete=models.CASCADE,
)
def __str__(self):
return "Member {} is blocking Member {}".format(self.source, self.target)
I started out with the Member and MemberRelaton classes and my migrations ran without any errors. But after I add the MemberBlock class and a blocks ManyToMany field in my Member model, I'm getting the following error when I run the makemigrations command which I don't understand:
You are trying to change the nullable field 'source' on memberrelation to non-nullable 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...
2) Ignore for now, and let me handle existing rows with NULL myself (e.g. because you added a RunPython or RunSQL operation to handle NULL values in a previous data migration)
3) Quit, and let me add a default in models.py
Select an option:
I don't understand this error because 1) it's talking about the MemberRelation class now that I've added a MemberBlock class. It didn't have a problem with this class previously; and 2) the error is saying that the 'source' field is nullable which I don't think it is.
Initially, I declared blocks without the through option and was getting the same error. I added the through option because I thought perhaps Django was getting confused by the fact that I have two recursive ManyToMany fields in one class.
What am I doing wrong?

Here is the solution:
blocked = models.ManyToManyField("self", symmetrical=False, related_name="members")

Related

What queryset should I do to select all One-to-one relations that are broken?

I have this 2 models :
class Asset(models.Model):
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
... some other fields ...
class AssetConstructorData(models.Model):
asset = models.OneToOneField(
Asset,
on_delete=models.CASCADE,
primary_key=True,
related_name='constructor_data',
)
... some other fields ...
For some historical reason, I have some One-to-one which are broken : some AssetConstructorData object has not their corresponding Asset object.
I would like to delete all AssetConstructorData objects that are broken.
Actually I have done :
for cd in AssetConstructorData.objects.all():
try:
cd.asset
except Asset.DoesNotExist:
cd.delete()
Which is definitively not optimized : Is that possible to make a queryset that selects all broken relations ?
I tried with .filter(asset=None) and .filter(asset__isnull=True) but it does not work.

You are trying to add a non-nullable field '_response_rendered'; django-markupfield

Upon wanting to add some Markdown to a project, I've decided to install django-markupfield to serve this objective. The only thing is that I've installed my models and associated fields ahead of making this decision; now replacing a TextField with MarkupField.
When attempting to make a migration to adjust for this, the following is brought up:
You are trying to add a non-nullable field '_response_rendered' to answer 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
What would be an appropriate default value to add to the fields that will be prompting for such? I have never been too sure of this question in general when it comes to using Django as well, so clarification would be appreciated.
https://github.com/jamesturk/django-markupfield
class Question(models.Model):
title = models.CharField(max_length=50)
body = MarkupField(
markup_type="markdown",
escape_html=True
)
dated = models.DateField(default=date.today)
user_account = models.ForeignKey(
'users.UserAccount',
on_delete=models.SET_NULL,
null=True, blank=True,
related_name="questions"
)
tags = models.ManyToManyField(Tag, related_name='questions')
objects = models.Manager()
dateranges = DateRangeQuerySet.as_manager()
status = QuestionStatusQuerySet.as_manager()
class Meta:
ordering = ['-dated']
default_manager_name = "objects"
def __str__(self):
return self.title
class Answer(models.Model):
question = models.ForeignKey(
Question,
on_delete=models.CASCADE,
related_name="answers"
)
response = MarkupField(
markup_type="markdown",
escape_html=True
)
dated = models.DateField(auto_now_add=True)
user_account = models.ForeignKey(
'users.UserAccount',
on_delete=models.SET_NULL,
null=True, blank=True,
related_name="answers"
)
You have to add default value.
In the background MarkupField create two fields (source link):
_rendered_field_name = lambda name: "_%s_rendered" % name # noqa
_markup_type_field_name = lambda name: "%s_markup_type" % name # noqa
class MarkupField(models.TextField):
...
def contribute_to_class(self, cls, name):
if self.rendered_field and not cls._meta.abstract:
...
cls.add_to_class(_markup_type_field_name(name), markup_type_field)
cls.add_to_class(_rendered_field_name(name), rendered_field)
super(MarkupField, self).contribute_to_class(cls, name)
setattr(cls, self.name, MarkupDescriptor(self))
The simple solution is provide default value at the time of migration:
So select choice 1 and enter default value. It will be add
default value only for the current migration, so your field is
non-nullable. Argument preserve_default(Django Docs) will be added.
Or provide default value in models.py:
class Question(models.Model):
...
body = MarkupField(
markup_type="markdown",
escape_html=True,
default="SOME VALUE"
)
...
class Answer(models.Model):
...
response = MarkupField(
markup_type="markdown",
escape_html=True,
default="SOME VALUE"
)
...
You can always remove default attribute and run makemigrations/migrate again.
THE BEST PRACTICE IS CREATE BACKUP BEFORE YOU RUN:
python manage.py makemigrations
python manage.py migrate
Try adding null = False to your field and migrate again. This usually works for me!

Django ERROR: there is no unique constraint matching given keys for referenced table

There are many similar questions on Stackoverflow, but none of the answers has helped me.
So, when running migrations, Django throws this error:
django.db.utils.ProgrammingError: there is no unique constraint matching given keys for referenced table "catalogues_movieslist"
I have this BlackWhiteCartoon model I am trying to create with the migration:
class BlackWhiteCartoon(BaseMovie, BaseList):
authors = JSONField(default=list, blank=True, null=True)
publisher = CharField(max_length=250, blank=True)
year = PositiveSmallIntegerField(blank=True, null=True)
This model inherits from the BaseList model that has only one field, a foreign key:
class BaseList(Model):
belongs_to_movies_list = ForeignKey(MoviesList, on_delete=CASCADE, null=True)
class Meta:
abstract = True
The referenced MoviesList model is the following:
class MoviesList(Model):
creation_date = DateTimeField(auto_now_add=True)
id = AutoField(primary_key=True)
uid = UUIDField(default=uuid.uuid4, editable=False)
user = ForeignKey(User, on_delete=CASCADE, null=True, blank=True)
Also, the BaseMovie model is the following:
class BaseMovie(BaseObject):
language = CharField(max_length=2, blank=True)
note = CharField(max_length=700, blank=True)
class Meta:
abstract = True
And the BaseObject model:
class BaseObject(Model):
id = BigAutoField(primary_key=True)
uid = UUIDField(default=uuid.uuid4, editable=False, db_index=True)
date_added = DateTimeField(null=True)
class Meta:
abstract = True
So, what I have tried:
Migrating everything together: this throws the above error.
Migrating BlackWhiteCartoon without inheriting BaseList first. This migration applies, but as soon as I add BaseList, it crashes with the same error.
Adding unique=True to the id field of MoviesList (both together with all other migrations and before adding BaseList to BlackWhiteCartoon). It throws the same error.
Adding to_field='id' together with 'unique=True'. The same error.
I have manually cleared the latest migrations from django_migrations in PSQL and removed the BlackWhiteCartoon table (to make sure that nothing prevents from migrating). To no avail either.
I have checked whether there are duplicated id's in the MoviesList table in the database. All id's are unique.
I have checked whether there are null foreign keys referencing to MoviesList in the database. There are none.
Also note that I have many models acting in absolutely the same way as BlackWhiteCartoon: Cartoon, Adventure, Action, etc. They are all functioning normally, yet they had been added several months ago.
Any ideas what I can do more?

How to make recursive django admin inline relationships?

I need to create an arbitrarily deep modular structure using Django admin. The nested_admin package has gotten me part of the way there, but there's a key piece of functionality that's still eluding me — the ability to create an object B inline off object A and then create another object A off that instance of object B. Here's a closer look at my models (simplified):
class ChatConditional(models.Model):
triggering_question = models.ForeignKey(
'texts.MessageChain',
on_delete=models.CASCADE,
)
keyword = models.CharField(max_length=50, blank=False, null=False)
keyword_response = models.ForeignKey(
'texts.MessageChain',
related_name='keyword_answer',
on_delete=models.CASCADE,
)
class MessageChain(models.Model):
conditional_trigger = models.ForeignKey(
'texts.ChatConditional',
blank=True, null=True,
)
message = models.TextField(max_length=160, blank=True, null=True)
So in the admin, I want to be able to create a ChatConditional to serve as a response to a certain MessageChain. Then once I create that Conditional, create another MessageChain to send that will potentially link to another Conditional. Here's what my admin models look like:
class SMSConditionalAdmin(nested_admin.NestedStackedInline):
model = ChatConditional
extra = 0
fk_name = "triggering_question"
inlines = [SMSChainAdmin]
class SMSChainAdmin(nested_admin.NestedStackedInline):
model = MessageChain
extra = 0
inlines = [SMSConditionalAdmin]
And now you can likely see the problem: In order to fully define the SMSConditionalAdmin, I need to set up an inline relationship to SMSChainAdmin, and vice versa. I might be missing something obvious, but how can I work around this need for the two admin inlines to recursively refer to one another?
Thanks in advance!

"already exists" error when using ManyToMany relationship between the same two models

class Product( models.Model ):
name = models.CharField(verbose_name="Name", max_length=255, null=True, blank=True)
the_products_inside_combo = models.ManyToManyField('self', verbose_name="Products Inside Combo", help_text="Only for Combo Products", blank=True)
However, I got this error when I tried to put the duplicate values:
From_product-to_product relationship with this From product and To
product already exists.
Screencap of the error.
Each pair (Product, Product) must be unique. This is why you get already exists error.
Behind the scenes, Django creates an intermediary join table to
represent the many-to-many relationship.
What do you want to do is to have many-to-many relationship between two models (nevermind that they are the same) with additional information stored - quantity (so you would have ProductA = 2x ProductB + ....
In order to model this relationship you will have to create intermediary model and use through option. Documentation explains it very well, so have a look:
https://docs.djangoproject.com/en/dev/topics/db/models/#intermediary-manytomany
Update
Here is minimal working example:
class Product(models.Model):
name = models.CharField(verbose_name='Name', max_length=255, null=True, blank=True)
products = models.ManyToManyField('self', through='ProductGroup', symmetrical=False)
def __str__(self):
return self.name
class ProductGroup(models.Model):
parent = models.ForeignKey('Product', related_name='+')
child = models.ForeignKey('Product', related_name='+')
and admin models:
class ProductGroupInline(admin.TabularInline):
model = Product.products.through
fk_name = 'parent'
class ProductAdmin(admin.ModelAdmin):
inlines = [
ProductGroupInline,
]
admin.site.register(Product, ProductAdmin)
admin.site.register(ProductGroup)
As you can see recursive Product-Product relation is modeled with ProductGroup (through parameter). Couple of notes:
Many-to-many fields with intermediate tables must not be symmetrical, hence symmetrical=False. Details.
Reverse accessors for ProductGroup are disabled ('+') (in general you can just rename them, however, you don't want to work with ProductGroup directly). Otherwise we would get Reverse accessor for 'ProductGroup.child' clashes with reverse accessor for 'ProductGroup.parent'..
In order to have a nice display of ManyToMany in admin we have to use inline models (ProductGroupInline). Read about them in documentation. Please note, however, fk_name field. We have to specify this because ProductGroup itself is ambiguous - both fields are foreign keys to the same model.
Be cautious with recurrency. If you would define, for example, __str__ on Product as: return self.products having ProductGroup with the same parent as the child you would loop infinitely.
As you can see in the screencap pairs can be duplicated now. Alternatively you would just add quantity field to ProductGroup and check for duplication when creating objects.