Add a foreign key to AbstractUser - python

I have this two models:
class User(AbstractUser):
pass
class Listing(models.Model):
listing_id = models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')
title = models.CharField(max_length=64)
owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="listing")
I would like to do the following in the User model:
class User(AbstractUser):
listing = models.ForeignKey(Listing, on_delete=models.CASCADE)
But i'm getting the following error:
NameError: name 'Listing' is not defined
Is it possible to add a foreign key to abstractuser?

Since you define User before Listing, the name is not yet defined. This is however not a problem for Django since you can use a string literal as well:
class User(AbstractUser):
listing = models.ForeignKey('Listing', on_delete=models.CASCADE)
class Listing(models.Model):
listing_id = models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name='ID'
)
title = models.CharField(max_length=64)
owner = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name='listing'
)
You can however not define a ForeignKey with the name listing here, since you already have an ForeignKey in your Listing model with as related_name='listing'. This would thus result in an error. You thus should either change the related_name, or rename the listing in the User model.

Pass the model name as a string to avoid issues where you want to reference a model that is not yet defined
class User(AbstractUser):
listing = models.ForeignKey('Listing', on_delete=models.CASCADE)
You can also use the same method to refer to a model in another app by adding the app name
listing = models.ForeignKey('app.Listing', on_delete=models.CASCADE)

Related

Django: how to set ForeignKey related_name in Abstract Model class?

I want to create on Abstract Model class for future inheriting like this:
class AbstractModel(models.Model):
created_at = models.DateTimeField(
auto_now_add=True,
blank=True,
null=True,
)
created_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.SET_NULL,
related_name='XXX_created_by',
blank=True,
null=True,
)
class Meta:
abstract = True
Field 'created_at' is working fine, but how to generate related_name in 'created_by' for my child classes to prevent clashing?
As the Be careful with related_name and related_query_name section of the documentation says, you can:
To work around this problem, when you are using related_name or related_query_name in an abstract base class (only), part of the value should contain '%(app_label)s' and '%(class)s'.
'%(class)s' is replaced by the lowercased name of the child class that the field is used in.
'%(app_label)s' is replaced by the lowercased name of the app the child class is contained within. Each installed application name must be unique and the model class names within each app must also be unique, therefore the resulting name will end up being different.
You thus can work with:
class AbstractModel(models.Model):
# …
created_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.SET_NULL,
related_name='%(class)s_created_by',
blank=True,
null=True,
)
class Meta:
abstract = True
Then the related_name will be foo_created_by if the name of the model that inherits is named foo.
Or if the same model name can occur in different apps:
class AbstractModel(models.Model):
# …
created_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.SET_NULL,
related_name='%(app_label)s_%(class)s_created_by',
blank=True,
null=True,
)
class Meta:
abstract = True
Then the related_name will be bar_foo_created_by if the name of the model that inherits is named foo in an app named bar.

Django: Making a value in a model unique specific to a user

I have a model for a project:
class Project(models.Model):
name = models.CharField(max_length=200, unique=True)
user = models.ForeignKey(User, on_delete=models.CASCADE)
I want to make the project name a unique value per user but at the moment if user 1 creates a name of "project 1" then user 2 is unable use that project name.
What is the best way to go about this?
Thanks!
unique_together is probably what you are looking for.
class Project(models.Model):
name = models.CharField(max_length=200)
user = models.ForeignKey(User, on_delete=models.CASCADE)
class Meta:
unique_together = (('name', 'user'),)

Django ManyToMany Field with an already existing table

What I'm trying to achieve is, having model Person that is created and managed by Django have a ManyToMany field with model Property that was "created" using inspectdb and already exists in the database.
(Property contains Geographical data and cannot be managed or changed by Django)
When trying to migrate, it raises :
ValueError: Related model 'cadastroapp.Property' cannot be resolved
Full stack here
Worth nothing that I removed from the migration file the step to create model Property, since it already exists and AFAIK there's no way to tell Django this in the model Class
models.py (simplified) :
class Person(models.Model):
objectid = models.AutoField(primary_key=True)
properties = models.ManyToManyField(
'Property',
through = 'Person_Property',
)
class Meta:
db_table = 'django_person'
class Person_Property(models.Model):
cod_person = models.ForeignKey('Person', on_delete=models.CASCADE)
cod_property = models.ForeignKey('Property', on_delete=models.CASCADE)
class Meta:
db_table = 'django_person_property'
class Property(models.Model):
objectid = models.BigIntegerField(unique=True, primary_key=True)
created_user = models.CharField(max_length=765, blank=True, null=True)
created_date = models.DateTimeField(blank=True, null=True)
last_edited_user = models.CharField(max_length=765, blank=True, null=True)
last_edited_date = models.DateTimeField(blank=True, null=True)
shape = models.TextField(blank=True, null=True) # This field type is a guess. - ESRI Shape
class Meta:
managed = False
db_table = '"GEO"."PROPERTY"'
There are a couple errors in your models.py file.
When defining a Foreignkey or ManytoMany field, you don't want the model name to be in quotes.
Please change:
class Person(models.Model):
properties = models.ManyToManyField(
'Property',
through = 'Person_Property',
)
and
class Person_Property(models.Model):
cod_person = models.ForeignKey('Person', on_delete=models.CASCADE)
cod_property = models.ForeignKey('Property', on_delete=models.CASCADE)
to:
class Person(models.Model):
properties = models.ManyToManyField(
Property,
through = 'Person_Property',
)
and
class Person_Property(models.Model):
cod_person = models.ForeignKey(Person, on_delete=models.CASCADE)
cod_property = models.ForeignKey(Property, on_delete=models.CASCADE)
then delete your migration file cadastroapp.0006_auto_20161122_1533.
then run makemigrations and migrate again.
This may still not migrate without errors, but it will get us on the right track.
I think that you want to put the model name in quotes. In case you leave it without quotes you have to ensure that the model is defined before the ManyToMany field has been defined. So you will need to have first class Property and then class Person in your file. When you put model name as "Property" then you do not need to care about order of class definitions.

Multiple foreign key fields in abstract Django class

I have an abstract base class that declares two foreign key fields to the user model:
class BaseModel(models.Model):
updated = models.DateTimeField(null=True)
updated_by = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, related_name="updated_by")
created = models.DateTimeField(null=True)
created_by = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, related_name="created_by")
class Meta:
abstract=True
I have multiple classes that inherit from this class. When I run makemigrations, I get the following error for each possible class-pair and for both created_by and updated_by:
myapp.ClassA.updated_by: (fields.E305) Reverse query name for 'ClassB.updated_by' clashes with reverse query name for 'ClassB.updated_by'.
HINT: Add or change a related_name argument to the definition for 'ClassA.updated_by' or 'ClassB.updated_by'.
Even though I already have a related_name set. It works fine with just one of the two foreign key fields declared.
Is it possible to have two foreign key fields to the same model in an abstract class, and if so, how do I set it up?
This is the expected behavior as mentioned in the documentation.
To work around this problem, when you are using related_name in an abstract base class (only), part of the name should contain '%(app_label)s' and '%(class)s'.
class BaseModel(models.Model):
updated = models.DateTimeField(null=True)
updated_by = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, related_name="updated%(app_label)s_%(class)s_related")
created = models.DateTimeField(null=True)
created_by = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, related_name="created%(app_label)s_%(class)s_related")
class Meta:
abstract=True
Since you use the related_name more than once, in model classes you inherit, then related name for the user model is not clear and clashes.
You will have to set a different related_name for each model.

How to reverse access ManyToManyField of instance of a model

class User(models.Model):
username = models.CharField(max_length=30)
email = models.CharField(max_length=60, primary_key=True)
registration_date = models.DateField(default=datetime.date.today)
class Task(models.Model):
author = models.ForeignKey(
User, on_delete=models.PROTECT,
related_name="%(app_label)s_%(class)s_author"
)
likes = models.ManyToManyField(
User,
related_name="%(app_label)s_%(class)s_like"
)
dislikes = models.ManyToManyField(
User,
related_name="%(app_label)s_%(class)s_dislike"
)
subscribed_users = models.ManyToManyField(User)
I want to make a method for User, which returns all tasks liked or disliked by that user. I looked through django orm's documentation and didn't find how I can, given an instance of User, find all Tasks he liked.
You just need to use the related_name like:
some_user.app_label_class_like.all()

Categories