CircularDependencyError by inherting FK from Abstract Class - django

I have the following abstract class:
class UserStamp(models.Model):
created_by = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True,
related_name='%(app_label)s_%(class)s_created_by', on_delete=models.CASCADE)
updated_by = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True,
related_name='%(app_label)s_%(class)s_updated_by', on_delete=models.CASCADE)
class Meta:
abstract = True
The Account Model inherits from it:
class Account(UserStamp):
pass
And I have a User Model with a FK to
class User(AbstractBaseUser,PermissionsMixin, UserStamp):
account = models.ForeignKey(Account, blank=True, null=True, related_name='owner',on_delete=models.CASCADE)
I have the following error when I migrate:
django.db.migrations.exceptions.CircularDependencyError:
The circular error I think appears because:
Account is calling User by inheriting created_by, updated_by from UserStamp, so points to User, and User points with Account FK back to Account.
If I use:
account = models.ForeignKey('accounts.Account', blank=True, null=True, related_name='owner',on_delete=models.CASCADE)
the issues is not solved.
My problem has (2 issues), but I decided to split the initial question in 2 questions to be more clear(I think they can be solved separately):
Second part here (how to set created_by, updated_by)

You can set a default user by modifying the save() method of your model:
class UserStamp(models.Model):
created_by = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True,
...
def save(self, *args, **kwargs):
if not self.created_by:
super_users = settings.AUTH_USER_MODEL.objects.filter(
is_superuser=True).order_by('date_joined')
first_user = super_users[0]
self.created_by = first_user
super().save(*args, **kwargs)
But I don't think that's what's causing your migration error. You may want to try a OneToOneField

Related

Constraining instantiation of a regular class based on foreginkeys to a regular class and an abstract class

I am creating my first django project and after having created a model A I realize that I want to create other models B, C ... that have certain traits in common with A but which needs to be separat models. Thus I created an abstract class 'InteractableItem' with these traits.
I want users to be able to like interactable items, however I also want to constrain the instantiation of a 'Like' model such that each user only can like a given interactable item once. To solve this I tried creating a models.UniqueConstraint in the like model between the fields 'user' and 'interactableitem'. This gave me the following error
ERRORS:
feed.Like.interactableitem: (fields.E300) Field defines a relation with model 'InteractableItem', which is either not installed, or is abstract.
feed.Like.interactableitem: (fields.E307) The field feed.Like.interactableitem was declared with a lazy reference to 'feed.interactableitem', but app 'feed' doesn't provide model 'interactableitem'.
I realise that my error is the referencing of an abstract class through a ForeignKey, however I dont see a way to constrain the instantiation of the like if the 'user' liking and the 'interactableitem' being like are not both fields in 'like'. This is where I need help.
How do you establish an instantiation constraint on such a 'Like' model?
Here I provide my referenced models:
class InteractableItem(models.Model):
date_created = models.DateTimeField(auto_now_add=True)
date_update = models.DateTimeField(auto_now=True)
author = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True)
class Meta:
abstract = True
class Like(models.Model):
date_created = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True)
interactableitem = models.ForeignKey(InteractableItem, on_delete=models.CASCADE, null=True, blank=True)
class Meta:
constraints = [
models.UniqueConstraint(fields=['user', 'interactableitem'], name='user_unique_like'),
]
After having tested different ways to constrain the instatiation of the 'like' model I realised that there was a way around this.
I let the abstract class 'BaseItem' have a ForeignKey to a regular class 'interactableitem'. 'Like' now has a models.Uniqueconstrait with two ForeignKey fields to regular classes which works. This way I just have to makesure that interactableitem is only created once, I'm sure there are better ways of doing this but this if self.pk should work for all scenarios I can think of, as it checks if the item is in the database before creation.
class InteractableItem(models.Model):
pass
class SomeItem(models.Model):
date_created = models.DateTimeField(auto_now_add=True)
date_update = models.DateTimeField(auto_now=True)
interactableitem = models.ForeignKey(InteractableItem, on_delete=models.CASCADE, null=True, blank=True)
author = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True)
class Meta:
abstract = True
def save(self, *args, **kwargs):
if not self.pk:
self.interactableitem = InteractableItem.objects.create()
super(SomeItem, self).save(*args, **kwargs)
class Like(models.Model):
date_created = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True)
interactableitem = models.ForeignKey(InteractableItem, on_delete=models.CASCADE, null=True, blank=True)
class Meta:
constraints = [
models.UniqueConstraint(fields=['user', 'interactableitem'], name='user_unique_like'),
]

Many to many relation entries not added by .add() method in Django

I am having a problem regarding adding entry to a many to many relation field. I have the models as follows
class Address(models.Model):
id = models.AutoField(primary_key=True)
country = CountryField(blank_label='(select country)', blank=False, null=False, verbose_name='Country')
state = models.CharField(
max_length=50,
choices=STATE_CHOICES,
verbose_name='State',
blank=False,
null=False
)
...
class Volunteer(models.Model):
userID = models.OneToOneField(User, on_delete=models.CASCADE, to_field='id', primary_key=True, related_name='volunteer')
identificationNumber = models.CharField(max_length=50, unique=True, blank=False, null=False, verbose_name='Identification Number')
currentAddress = models.ManyToManyField(Address, related_name='volunteerCurrentAddress', verbose_name='Current Address', blank=False)
permanentAddress = models.ManyToManyField(Address, related_name='volunteerPermanentAddress', verbose_name='Permanent Address', blank=False)
...
def save(self, *args, **kwargs):
self.slug = self.userID.username
super(Volunteer, self).save(*args, **kwargs)
class TemporaryVolunteer(Volunteer):
pass
And in the views, I get both the currentAddress and permanentAddress fields as a ManyToManyRelatedManager. They are temporaryVolunteer.currentAddress and temporaryVolunteer.permanentAddress. I use these to create a new Volunteer instance as
volunteer = Volunteer(...)
volunteer.save()
volunteer.currentAddress.add(temporaryVolunteer.currentAddress.all()[0])
volunteer.permanentAddress.add(temporaryVolunteer.permanentAddress.all()[0])
volunteer.save()
But when I do print(volunteer.currentAddress.all()) or print(volunteer.permanentAddress.all()), it returns an empty queryset. I also checked the admin site for confirmation and there are no entries of address on the volunteer instance.
Is there any way the entries can be added with this approach?
The problem was in the design of the database. I had used a new class TemporaryVolunteer in order to store unverified accounts and later moved to the Volunteer class after they were verfied. As I had inherited TemporaryVolunteer from Volunteer class, the way Django handles Many to many relationships without making duplicates (Source: https://docs.djangoproject.com/en/3.0/topics/db/examples/many_to_many/) led to the problem of no entries being added in the corresponding Volunteer class.
Initially, I checked this by copying all the members of Volunteer to TemporaryVolunteer. After verifying that it worked, I changed the database design as this is a bad approach and kept a boolean value isVerified in the Volunteer class and removed the TemporaryVolunteer class entirely.

django checkbox select multiple models

Hi I have the following django model:
class Issue(models.Model):
title = models.CharField(max_length=200)
date = models.DateTimeField(auto_now=True)
assignee = models.ForeignKey(User, on_delete=models.CASCADE, related_name='assignee')
owner = models.ForeignKey(User, on_delete=models.CASCADE, related_name='owner', null=True, blank=True)
description = models.TextField()
state = models.IntegerField(choices=STATUS_CHOICES, default=1)
priority = models.IntegerField(choices=RELEVANCE_CHOICES, default=2)
expired_date = models.DateField(auto_now=False, null=True, blank=True)
and a form which allow a user to create an Issue instance:
class IssueForm(forms.ModelForm):
class Meta:
model = Issue
fields = ('title', 'description', 'assignee', 'state', 'priority', 'expired_date')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['title'].label = "Titolo"
self.fields['description'].label = "Descrizione"
self.fields['state'].label = "Stato"
self.fields['priority'].label = "Priorità"
self.fields['expired_date'].label = "Termine"
self.fields['expired_date'].widget.attrs.update({'class': 'datepicker'})
self.fields['assignee'] = forms.MultipleChoiceField(
choices=self.fields['assignee'].choices,
widget=forms.CheckboxSelectMultiple,
label=("Assegnatario")
)
def clean(self):
cleaned_data = super().clean()
user_id = [i for i in cleaned_data['assignee']]
cleaned_data['assignee'] = [User.objects.get(id=i) for i in user_id]
return cleaned_data
I render this form and the field assignee is a checkbox.
I would like to be able to choose several assignee for the same issue, but I got an error because the Issue model expect just one User instance
How can I modify my model Issue in order to get more than one user ?
Thanks
you can create a new class and name it Issue_Instance where every Issue Object can have an assignee as a foreign key the problem that the relation is one to many because you have to choose more than one assignee and Django doesn't support the idea of having Array or List of Foreign Keys(I don't know any frame works that do :=) ) so I would suggest creating a new class or make the foreign key relation one-to-many key field read about it it will be very useful to solve your problem

Stop Django from autofilling manytomany fields in admin

I have the following models:
class Color(models.Model):
name = models.CharField(max_length=50, null=False, blank=False)
def __str__(self):
return self.name
class Flower(models.Model):
flower_number = models.PositiveSmallIntegerField(
default=1,blank=True, null=True)
petal_color = models.ManyToManyField(Color,blank=True, related_name="%(app_label)s_%(class)s_petal",
related_query_name="%(app_label)s_%(class)s")
petal_outer_color = models.ManyToManyField(Color,blank=True, related_name="%(app_label)s_%(class)s_petal_outer",
related_query_name="%(app_label)s_%(class)s")
class Meta:
abstract = True
class Plant(Flower):
name = models.CharField(max_length=50, null=False, blank=False, unique=True)
On the Admin I just have:
admin.site.register(Plant)
When I go into the Django admin and fill out either of the manytomany petal_color or petal_outer_color with data the other manytomany field automatically gets filled when it saves. How do I stop this from happening? Nothing shows up as an error and I tried going back and deleting and re-entering data but it still happens
Try using symmetrical=False in the ManyToManyField, that might be causing the issue here as you have two M2M fields going to the same model.
Read up on symmetrical in the Django docs.
Do something like this
class Flower(models.Model):
flower_number = models.PositiveSmallIntegerField(
default=1,blank=True, null=True)
petal_color = models.ManyToManyField(Color,blank=True, symmetrical=False related_name="%(app_label)s_%(class)s_petal",
related_query_name="%(app_label)s_%(class)s")
petal_outer_color = models.ManyToManyField(Color,blank=True, symmetrical=False, related_name="%(app_label)s_%(class)s_petal_outer",
related_query_name="%(app_label)s_%(class)s")
class Meta:
abstract = True
By default, the value of symmetrical is True for Many to Many Field which is a bi-directional relationship.
The ManyToManyField is assumed to be symmetrical – that is, if I am your friend, then you are my friend.

Django Nested Form - Always Showing Object instead of model details

I'm working on a Django project generated via Mezzanine. I've been able to create my models, however I want to have a form where an admin can select from a list to assign a value in a many to many or a one to many relationship. For example, I have a model for Schemas:
class Schema(AutoCreatedUpdatedMixin, SoftDeleteMixin):
"""List of all Schemas in a given database"""
name = models.CharField(max_length=128, null=False)
status = models.BooleanField(max_length=128, null=False, default=True, verbose_name="Is Active")
description = models.CharField(max_length=65535, null=True, blank=True, default=None)
database = models.ForeignKey(Database, on_delete=models.CASCADE)
pull_requests = models.ManyToManyField(Link)
questions = models.ManyToManyField(Question, blank=True)
comments = models.ManyToManyField(Comment, blank=True)
technical_owners = models.ManyToManyField(Employee, related_name='technical_owners_schemas', blank=True)
business_owners = models.ManyToManyField(Employee, related_name='business_owners_schemas', blank=True)
watchers = models.ManyToManyField(Employee, related_name='watchers_schemas', blank=True)
def __unicode__(self):
return "{}".format(self.name)
And I have a model for Employees
class Employee(AutoCreatedUpdatedMixin, SoftDeleteMixin):
"""List of people with any involvement in tables or fields: business or technical owners, developers, etc"""
name = models.CharField(max_length=256, blank=False, null=False, default=None, unique=True)
email = models.EmailField(blank=True, null=True, unique=True)
def __unicode__(self):
return "{}".format(self.employee)
An employee can own multiple schemas and a schema can be owned by multiple employees. My database has an active employee in it, however when I try to create a Schema the employee shows up as Employee Object. Rather I would want the form to show the Employee.name. How can I do this? My admin file contains the following:
class SchemasAdmin(admin.ModelAdmin):
list_display = ['name', 'status', 'database', 'description']
ordering = ['status', 'database', 'name']
actions = []
exclude = ('created_at', 'updated_at', 'deleted_at')
First of all are you using python 2 or 3? For 3, the __str__ method should be used instead of __unicode__. I am writing this because it seems that there's a problem with the __unicode__ method of Employee, which although is defined as:
def __unicode__(self):
return "{}".format(self.employee)
th Employee class does not have an employee attribute (unless there's such an attribute in the mixins that class inherits from (AutoCreatedUpdatedMixin, SoftDeleteMixin) but I don't think that is the case.
In any case, the problem is that you haven't defined a propery __str__ (if using python 3) or __unicode__ (for python 2) method on the Employee class - just define it like:
return self.name
and you should see the employee's name in the django admin select fields.