I am trying to use the "limit_choices_to" functionality in a Django OneToOneField where upstream of what I want to limit the choices on is another ForeignKey. The error I get in the admin with the way I have it set up is:
invalid literal for int() with base 10: 'Storage Array'
I assume this is because it is looking at the actual value in the column asset_type which is an integer foreign key. I need to be able to limit the choices based on a field value of the foreign key as opposed to the key value itself.
Basically what I am trying to accomplish is having the admin area (and other forms) only allow you to choose a valid asset type when adding a new asset. For example, if I am adding a "storage array" the upstream asset tied to it should only be allowed to be asset_type of storage array.
Here is my model:
from django.db import models
from localflavor.us.us_states import STATE_CHOICES
# Table of brand names of assets
class Brand(models.Model):
brand = models.CharField(max_length=128, unique=True)
def __unicode__(self):
return self.brand
# Table of device types for assets, e.g. "Storage Array"
class Device(models.Model):
device_type = models.CharField(max_length=128, unique=True)
device_url_slug = models.SlugField(max_length=70, unique=True)
class Meta:
verbose_name = "device type"
verbose_name_plural = "device types"
def __unicode__(self):
return self.device_type
# Table of asset locations
class Location(models.Model):
site_name = models.CharField(max_length=128, unique=True)
site_nick = models.CharField(max_length=5, unique=True)
address_line_one = models.CharField(max_length=256)
address_line_two = models.CharField(max_length=256, null=True, blank=True)
address_city = models.CharField(max_length=32)
address_state = models.CharField(max_length=2, choices=STATE_CHOICES, null=True, blank=True)
address_zip = models.CharField(max_length=5)
def __unicode__(self):
return self.site_name
# Table of Environments, e.g. "Production"
class Environment(models.Model):
environment = models.CharField(max_length=128, unique=True)
def __unicode__(self):
return self.environment
class Credentials(models.Model):
AUTH_CHOICES = (
('SSH', 'Standard SSH'),
('SSH-Key', 'SSH with Key-based login'),
('HTTP', 'Standard HTTP'),
('HTTPS', 'Secure HTTP'),
('API', 'API Based'),
('SNMP', 'SNMP Based'),
)
SNMP_VERSIONS = (
('v1', 'SNMP v1'),
('v3', 'SNMP v3'),
)
auth_method = models.CharField(max_length=32, choices=AUTH_CHOICES)
auth_username = models.CharField(max_length=32)
auth_password = models.CharField(max_length=32, null=True, blank=True)
auth_snmp_version = models.CharField(max_length=2, choices=SNMP_VERSIONS, null=True, blank=True)
auth_snmp_community = models.CharField(max_length=128, null=True, blank=True)
class Meta:
verbose_name = "credentials"
verbose_name_plural = "credentials"
def __unicode__(self):
return self.auth_method
class Asset(models.Model):
asset_name = models.CharField(max_length=128, unique=True)
asset_type = models.ForeignKey(Device)
brand = models.ForeignKey(Brand)
model = models.CharField(max_length=128)
serial = models.CharField(max_length=256)
location = models.ForeignKey(Location)
environment = models.ForeignKey(Environment)
datacenter_room = models.CharField(max_length=32, null=True, blank=True)
grid_location = models.CharField(max_length=32, null=True, blank=True)
mgmt_address = models.CharField(max_length=128)
notes = models.TextField(null=True, blank=True)
def __unicode__(self):
return self.asset_name
class StorageArray(models.Model):
OS_NAME_CHOICES = (
('Data OnTap', 'NetApp Data OnTap'),
)
OS_TYPE_CHOICES = (
('7-Mode', 'NetApp 7-Mode'),
('cDOT', 'NetApp Clustered'),
)
HA_TYPE_CHOICES = (
('Standalone', 'Standalone System'),
('HA Pair', 'HA Pair'),
('Clustered', 'Clustered'),
)
asset = models.OneToOneField(Asset, primary_key=True, limit_choices_to={'asset_type': 'Storage Array'})
os_name = models.CharField(max_length=32, choices=OS_NAME_CHOICES, null=True, blank=True)
os_version = models.CharField(max_length=16, null=True, blank=True)
os_type = models.CharField(max_length=16, choices=OS_TYPE_CHOICES, null=True, blank=True)
array_ha_type = models.CharField(max_length=32, choices=HA_TYPE_CHOICES, null=True, blank=True)
array_partner = models.CharField(max_length=128, null=True, blank=True)
credentials = models.ForeignKey(Credentials, null=True, blank=True)
class Meta:
verbose_name = "storage array"
verbose_name_plural = "storage arrays"
def __unicode__(self):
return self.asset.asset_name
class SANSwitch(models.Model):
OS_NAME_CHOICES = (
('FabricOS', 'Brocade FabricOS'),
)
SWITCH_TYPE_CHOICES = (
('Standalone', 'Standalone Switch'),
('Director', 'Director'),
('Router', 'Multiprotocol Router'),
('Blade', 'Blade Chassis IO Module'),
)
SWITCH_ROLE_CHOICES = (
('Core', 'Core'),
('Edge', 'Edge'),
('AG', 'Access Gateway'),
)
asset = models.OneToOneField(Asset, primary_key=True, limit_choices_to={'asset_type': 'SAN Switch'})
os_name = models.CharField(max_length=32, choices=OS_NAME_CHOICES, null=True, blank=True)
os_version = models.CharField(max_length=16, null=True, blank=True)
switch_type = models.CharField(max_length=32, choices=SWITCH_TYPE_CHOICES, null=True, blank=True)
switch_role = models.CharField(max_length=32, choices=SWITCH_ROLE_CHOICES, null=True, blank=True)
credentials = models.ForeignKey(Credentials, null=True, blank=True)
class Meta:
verbose_name = "san switch"
verbose_name_plural = "san switches"
def __unicode__(self):
return self.asset.asset_name
I fixed it all by myself!
It seems to translate further down a relationship you can make use of the double underscore notation that python/django has built in.
To fix my issue:
asset = models.OneToOneField(Asset, primary_key=True, limit_choices_to={'asset_type': 'Storage Array'})
became:
asset = models.OneToOneField(Asset, primary_key=True, limit_choices_to={'asset_type__device_type': 'Storage Array'})
Related
I was trying to delete my Apllication model:
class Application(models.Model):
app_type = models.ForeignKey(ApplicationCategory, on_delete=models.CASCADE, related_name='applications')
fio = models.CharField(max_length=40)
phone_number = models.CharField(max_length=90)
organisation_name = models.CharField(max_length=100, null=True, blank=True)
aid_amount = models.PositiveIntegerField()
pay_type = models.CharField(max_length=1, choices=PAY_CHOICES, default=PAY_CHOICES[0][0])
status = models.ForeignKey(AppStatus, on_delete=models.CASCADE, related_name='applications', null=True, blank=True)
created = models.DateTimeField(auto_now_add=True)
benefactor = models.ForeignKey(Benefactor, on_delete=models.CASCADE, related_name='applications', null=True)
def __str__(self):
return f"id={self.id} li {self.fio} ning mablag\'i!"
and this was my Benefactor model:
class Benefactor(models.Model):
fio = models.CharField(max_length=255)
phone_number = models.CharField(max_length=9)
image = models.ImageField(upload_to='media/')
sponsory_money = models.IntegerField()
organisation_name = models.CharField(max_length=55, null=True, blank=True)
def __str__(self):
return f"{self.fio}"
But I got the below message on superAdmin Panel:
TypeError at /admin/api/benefactor/
create_reverse_many_to_one_manager.\<locals\>.RelatedManager.__call__() missing 1 required keyword-only argument: 'manager'
I would expect delete smoothly!!
Your Benefactor model has several ForeignKey relationships that share the related_name. Give each a unique name and rerun your migrations.
I have created a Django filter set to filter data, some fields are filtered with relationships.
When I never filter with the endpoint, it just returns all data instead of filtered data, what could be wrong here?
This is my endpoint filterer :
http://127.0.0.1:5000/api/v1/qb/questions/?paper=26149c3b-c3e3-416e-94c4-b7609b94182d§ion=59bdfd06-02d4-4541-9478-bf495dafbee1&topic=df8c2152-389a-442f-a1ce-b56d04d39aa1&country=KE
Below is my sample :
from django_filters import rest_framework as filters
class QuestionFilter(filters.FilterSet):
topic = django_filters.UUIDFilter(label='topic',
field_name='topic__uuid',
lookup_expr='icontains')
sub_topic = django_filters.UUIDFilter(label='sub_topic',
field_name='topic__sub_topic__uuid',
lookup_expr='icontains')
paper = django_filters.UUIDFilter(label='paper',
field_name='paper__uuid',
lookup_expr='icontains')
section = django_filters.UUIDFilter(label='section',
field_name='section__uuid',
lookup_expr='icontains')
subject = django_filters.UUIDFilter(label='subject',
field_name="paper__subject__id",
lookup_expr='icontains'
)
year = django_filters.UUIDFilter(label='year',
field_name='paper__year__year',
lookup_expr="icontains")
country = django_filters.CharFilter(label='country',
field_name="paper__exam_body__country",
lookup_expr='icontains')
class Meta:
model = Question
fields = ['topic', 'section', 'paper', 'sub_topic', 'subject', 'year',
'country']
Then my view is like this :
class QuestionView(generics.ListCreateAPIView):
"""Question view."""
queryset = Question.objects.all()
serializer_class = serializers.QuestionSerializer
authentication_classes = (JWTAuthentication,)
filter_backends = (filters.DjangoFilterBackend,)
filterset_class = QuestionFilter
Then the models attached to the filter are as below :
class Question(SoftDeletionModel, TimeStampedModel, models.Model):
"""Questions for a particular paper model."""
uuid = models.UUIDField(unique=True, max_length=500,
default=uuid.uuid4,
editable=False,
db_index=True, blank=False, null=False)
mentor = models.ForeignKey(User, related_name='question_mentor', null=True,
on_delete=models.SET_NULL)
paper = models.ForeignKey(Paper, max_length=25, null=True,
blank=True, on_delete=models.CASCADE)
question = models.TextField(
_('Question'), null=False, blank=False)
section = models.ForeignKey(QuestionSection,
related_name='section_question',
null=True, on_delete=models.SET_NULL)
topic = models.ForeignKey(Course, related_name='topic_question',
null=True, on_delete=models.SET_NULL)
question_number = models.IntegerField(_('Question Number'), default=0,
blank=False, null=False)
image_question = models.ImageField(_('Image question'),
upload_to='image_question',
null=True, max_length=900)
answer_locked = models.BooleanField(_('Is Answer locked'), default=True)
status = models.CharField(max_length=50, choices=QUESTION_STATUSES,
default=ESSAY)
address_views = models.ManyToManyField(CustomIPAddress,
related_name='question_views',
default=None, blank=True)
bookmarks = models.ManyToManyField(User, related_name='qb_bookmarks',
default=None, blank=True)
def __str__(self):
return f'{self.question}'
Paper Model
class Paper(SoftDeletionModel, TimeStampedModel, models.Model):
"""Paper model."""
uuid = models.UUIDField(unique=True, max_length=500,
default=uuid.uuid4,
editable=False,
db_index=True, blank=False, null=False)
subject = models.ForeignKey(Subject, related_name='subject',
null=True, on_delete=models.SET_NULL)
mentor = models.ForeignKey(User, related_name='paper_mentor', null=True,
on_delete=models.SET_NULL)
year = models.DateField(_('Year'), blank=False, null=False)
grade_level = models.ForeignKey(ClassGrade, related_name='grade_paper',
null=True, on_delete=models.SET_NULL)
exam_body = models.ForeignKey(ExamBody, related_name='exam_body_paper',
null=True, on_delete=models.SET_NULL)
number_of_questions = models.IntegerField(_('No of questions'),
blank=False, null=False)
number_of_sections = models.IntegerField(_('No of sections'),
blank=False, null=False)
color_code = ColorField(format='hexa', default='#33AFFF', null=True)
class Meta:
ordering = ['created']
def __str__(self):
return f'{self.subject.name} ({self.year})'
QuestionSection Model :
class QuestionSection(SoftDeletionModel, TimeStampedModel, models.Model):
"""Question paper sections e.g Section A, B, C etc."""
uuid = models.UUIDField(unique=True, max_length=500,
default=uuid.uuid4,
editable=False,
db_index=True, blank=False, null=False)
section = models.CharField(
_('Question Section'), max_length=100, null=False, blank=False)
def __str__(self):
return f'{self.section}'
class Course(SoftDeletionModel, TimeStampedModel, models.Model):
"""
Topic model responsible for all topics.
"""
uuid = models.UUIDField(unique=True, max_length=500,
default=uuid.uuid4,
editable=False,
db_index=True, blank=False, null=False)
title = models.CharField(
_('Title'), max_length=100, null=False, blank=False)
overview = models.CharField(
_('Overview'), max_length=100, null=True, blank=True)
description = models.CharField(
_('Description'), max_length=200, null=False, blank=False
)
country = CountryField()
subject = models.ForeignKey(Subject, on_delete=models.CASCADE)
topic_cover = models.ImageField(
_('Topic Cover'), upload_to='courses_images',
null=True, blank=True, max_length=900)
grade_level = models.ForeignKey(
ClassGrade, max_length=25, null=True,
blank=True, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
ranking = models.IntegerField(
_('Ranking of a Topic'), default=0, help_text=_('Ranking of a Topic')
)
def __str__(self):
return self.title
class Meta:
verbose_name_plural = "Topics"
ordering = ['ranking']
I'm trying to get the object "Book" from prommotion. Book is a ForeignKey in "prommotion", and I filtered all the prommotions that are active. I need to get the "Book" object from the Prommotion if its active and return it.
(And I know promotion is spelled wrong)
Views:
class Book_PrommotionViewSet(viewsets.ViewSet):
def list(self, request):
queryset = Prommotion.objects.filter(active=True)
serializer = PrommotionSerializer(queryset, many=True)
return Response(serializer.data, HTTP_200_OK)
Prommotion Model:
class Prommotion(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
precent = models.DecimalField(decimal_places=2, max_digits=255, null=True, blank=True)
active = models.BooleanField(default=False)
date_from = models.DateField()
date_to = models.DateField()
book = models.ForeignKey(Book, on_delete=models.SET_NULL, null=True, blank=True)
country = models.ForeignKey(Country, on_delete=models.SET_NULL, null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
verbose_name = 'Prommotion'
verbose_name_plural = 'Prommotions'
Book Model:
class Book(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
title = models.CharField(max_length=255, null=True, blank=True)
author = models.ForeignKey(Author, on_delete=models.SET_NULL, null=True, blank=True)
price = models.DecimalField(decimal_places=2, max_digits=255)
published = models.DateField()
edition = models.CharField(max_length=255)
isbn_code = models.CharField(max_length=255)
pages = models.IntegerField(blank=True, null=True, default=0)
description = models.TextField(null=True, blank=True)
cover = models.CharField(max_length=30, choices=Cover.choices(), default=None, null=True, blank=True)
genre = models.CharField(max_length=30, choices=Genre.choices(), default=None, null=True, blank=True)
language = models.CharField(max_length=30, choices=Language.choices(), default=None, null=True, blank=True)
format = models.CharField(max_length=30, choices=Format.choices(), default=None, null=True, blank=True)
publisher = models.CharField(max_length=30, choices=Publisher.choices(), default=None, null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.title
class Meta:
verbose_name = 'Book'
verbose_name_plural = 'Books'
The first way to get all Books that are related to your active promotions is to extract the book ids from the queryset and pass it to a Book filter
active_promotions = Prommotion.objects.filter(active=True)
Book.objects.filter(id__in=active_promotions.values('book_id'))
Or simply filter books with active promotions by using the double underscore syntax to follow relationships
Book.objects.filter(prommotion__active=True).distinct()
I have a Project and Memberships model where each project has a list of members. I need to find out if the current logged in user is listed as a member on the project from the project serializer, but I can't seem to get the request correct in my get_i_am_member method. I need to pass the project id from the Project model to the Membership model to filter the data, then check if the user in the filtered memberships model matches the user making the request. Can someone please assist? Here is my code:
######################################################
# Serializer
######################################################
class ProjectSerializer(LightWeightSerializer):
id = Field()
name = Field()
slug = Field()
description = Field()
created_date = Field()
modified_date = Field()
owner = MethodField()
members = MethodField()
is_private = Field()
anon_permissions = Field()
public_permissions = Field()
is_looking_for_people = Field()
looking_for_people_note = Field()
i_am_member = MethodField()
i_am_admin = MethodField()
my_permissions = MethodField()
def get_members(self, project):
members = Membership.objects.filter(project_id=project.id).select_related()
return MembershipSerializer(members, many=True, context=self.context).data
def get_i_am_member(self, request):
members_list = Membership.objects.filter(project_id=request.project.id).select_related('user')
for member in members_list:
if member.user == request.username:
print(member.user)
print("True")
return True
else:
print(member.user)
print("False")
return False
######################################################
# Models
######################################################
class Project(models.Model):
name = models.CharField(max_length=250, null=False, blank=False,
verbose_name=_("name"))
slug = models.SlugField(max_length=250, unique=True, null=False, blank=True,
verbose_name=_("slug"))
description = models.TextField(null=False, blank=False,
verbose_name=_("description"))
logo = models.FileField(upload_to=get_project_logo_file_path,
max_length=500, null=True, blank=True,
verbose_name=_("logo"))
created_date = models.DateTimeField(null=False, blank=False,
verbose_name=_("created date"),
default=timezone.now)
modified_date = models.DateTimeField(null=False, blank=False,
verbose_name=_("modified date"))
owner = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True,
related_name="owned_projects", verbose_name=_("owner"), on_delete=models.CASCADE)
members = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name="projects",
through="Membership", verbose_name=_("members"),
through_fields=("project", "user"))
is_private = models.BooleanField(default=True, null=False, blank=True,
verbose_name=_("is private"))
anon_permissions = ChoiceArrayField(
models.TextField(null=False, blank=False, choices=ANON_PERMISSIONS),
null=True,
blank=True,
default=list,
verbose_name=_("anonymous permissions")
)
public_permissions = ChoiceArrayField(models.TextField(null=False, blank=False, choices=MEMBERS_PERMISSIONS),
null=True, blank=True, default=list, verbose_name=_("user permissions"))
is_featured = models.BooleanField(default=False, null=False, blank=True,
verbose_name=_("is featured"))
class Meta:
db_table = "projects"
verbose_name = "project"
verbose_name_plural = "projects"
ordering = ["name", "id"]
index_together = [
["name", "id"],
]
class Membership(models.Model):
# This model stores all project memberships. Also
# stores invitations to memberships that do not have
# assigned user.
user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, default=None,
related_name="memberships", on_delete=models.CASCADE)
project = models.ForeignKey(Project, null=False, blank=False,
related_name="memberships", on_delete=models.CASCADE)
role = models.ForeignKey('core.Role', null=False, blank=False,
related_name="memberships", on_delete=models.CASCADE)
is_admin = models.BooleanField(default=False, null=False, blank=False)
user_order = models.BigIntegerField(default=timestamp_ms, null=False, blank=False,
verbose_name=_("user order"))
class Meta:
db_table = "memberships"
verbose_name = "membership"
verbose_name_plural = "memberships"
unique_together = ("user", "project",)
ordering = ["project", "user__full_name", "user__username", "user__email"]
def get_related_people(self):
related_people = get_user_model().objects.filter(id=self.user.id)
return related_people
def clean(self):
# TODO: Review and do it more robust
memberships = Membership.objects.filter(user=self.user, project=self.project)
if self.user and memberships.count() > 0 and memberships[0].id != self.id:
raise ValidationError(_('The user is already member of the project'))
By default drf passes request object using context, so modification should be:
def get_i_am_member(self, project):
members_list = Membership.objects.filter(project_id=project.id).select_related('user')
for member in members_list:
if member.user == self.context['request'].user:
print(member.user)
print("True")
return True
else:
print(member.user)
print("False")
return False
But in case you manually call serializer, you need to pass it:
ProjectSerializer(instance=project, context={'request': request})
I have the following 4 models in my program - User, Brand, Agency and Creator.
User is a superset of Brand, Agency and Creator.
A user can be a brand, agency or creator. They cannot take on more than one role. Their role is defined by the accountType property. If they are unset (i.e. 0) then no formal connection exists.
How do I express this in my model?
User model
class User(models.Model):
id = models.UUIDField(primary_key=True, default=uuid4, editable=False)
email = models.EmailField(max_length=255, null=True, default=None)
password = models.CharField(max_length=255, null=True, default=None)
ACCOUNT_CHOICE_UNSET = 0
ACCOUNT_CHOICE_BRAND = 1
ACCOUNT_CHOICE_CREATOR = 2
ACCOUNT_CHOICE_AGENCY = 3
ACCOUNT_CHOICES = (
(ACCOUNT_CHOICE_UNSET, 'Unset'),
(ACCOUNT_CHOICE_BRAND, 'Brand'),
(ACCOUNT_CHOICE_CREATOR, 'Creator'),
(ACCOUNT_CHOICE_AGENCY, 'Agency'),
)
account_id = models.ForeignKey(Brand)
account_type = models.IntegerField(choices=ACCOUNT_CHOICES, default=ACCOUNT_CHOICE_UNSET)
class Meta:
verbose_name_plural = "Users"
def __str__(self):
return "%s" % self.email
Brand model
class Brand(models.Model):
id = models.UUIDField(primary_key=True, default=uuid4, editable=False)
name = models.CharField(max_length=255, null=True, default=None)
brand = models.CharField(max_length=255, null=True, default=None)
email = models.EmailField(max_length=255, null=True, default=None)
phone = models.CharField(max_length=255, null=True, default=None)
website = models.CharField(max_length=255, null=True, default=None)
class Meta:
verbose_name_plural = "Brands"
def __str__(self):
return "%s" % self.brand
Creator model
class Creator(models.Model):
id = models.UUIDField(primary_key=True, default=uuid4, editable=False)
first_name = models.CharField(max_length=255, null=True, default=None)
last_name = models.CharField(max_length=255, null=True, default=None)
email = models.EmailField(max_length=255, null=True, default=None)
youtube_channel_username = models.CharField(max_length=255, null=True, default=None)
youtube_channel_url = models.CharField(max_length=255, null=True, default=None)
youtube_channel_title = models.CharField(max_length=255, null=True, default=None)
youtube_channel_description = models.CharField(max_length=255, null=True, default=None)
photo = models.CharField(max_length=255, null=True, default=None)
youtube_channel_start_date = models.CharField(max_length=255, null=True, default=None)
keywords = models.CharField(max_length=255, null=True, default=None)
no_of_subscribers = models.IntegerField(default=0)
no_of_videos = models.IntegerField(default=0)
no_of_views = models.IntegerField(default=0)
no_of_likes = models.IntegerField(default=0)
no_of_dislikes = models.IntegerField(default=0)
location = models.CharField(max_length=255, null=True, default=None)
avg_views = models.IntegerField(default=0)
GENDER_CHOICE_UNSET = 0
GENDER_CHOICE_MALE = 1
GENDER_CHOICE_FEMALE = 2
GENDER_CHOICES = (
(GENDER_CHOICE_UNSET, 'Unset'),
(GENDER_CHOICE_MALE, 'Male'),
(GENDER_CHOICE_FEMALE, 'Female'),
)
gender = models.IntegerField(choices=GENDER_CHOICES, default=GENDER_CHOICE_UNSET)
class Meta:
verbose_name_plural = "Creators"
def __str__(self):
return "%s %s" % (self.first_name,self.last_name)
Agency model
class Agency(models.Model):
id = models.UUIDField(primary_key=True, default=uuid4, editable=False)
name = models.CharField(max_length=255, null=True, default=None)
agency = models.CharField(max_length=255, null=True, default=None)
email = models.EmailField(max_length=255, null=True, default=None)
phone = models.CharField(max_length=255, null=True, default=None)
website = models.CharField(max_length=255, null=True, default=None)
class Meta:
verbose_name_plural = "Agencies"
def __str__(self):
return "%s" % self.agency
Update:
So I've whittled it down to this bit here in the model:
ACCOUNT_CHOICE_UNSET = 0
ACCOUNT_CHOICE_BRAND = 1
ACCOUNT_CHOICE_CREATOR = 2
ACCOUNT_CHOICE_AGENCY = 3
ACCOUNT_CHOICES = (
(ACCOUNT_CHOICE_UNSET, 'Unset'),
(ACCOUNT_CHOICE_BRAND, 'Brand'),
(ACCOUNT_CHOICE_CREATOR, 'Creator'),
(ACCOUNT_CHOICE_AGENCY, 'Agency'),
)
account_type = models.IntegerField(choices=ACCOUNT_CHOICES, default=ACCOUNT_CHOICE_UNSET)
limit = models.Q(app_label='api', model='Brand') | \
models.Q(app_label='api', model='Creator') | \
models.Q(app_label='api', model='Agency')
content_type = models.ForeignKey(ContentType, limit_choices_to=get_content_type_choices(), related_name='user_content_type')
content_object = GenericForeignKey('content_type', 'email')
If account_type = 1 then link to brand model
If account_type = 2 then link to creator model
If account_type = 3 then link to agency model
How do I accomplish this? Getting this error:
File "/Users/projects/adsoma-api/api/models.py", line 7, in <module>
class User(models.Model):
File "/Users/projects/adsoma-api/api/models.py", line 28, in User
content_type = models.ForeignKey(ContentType, limit_choices_to=get_content_type_choices(), related_name='user_content_type')
NameError: name 'get_content_type_choices' is not defined
Have you tried exploring Django's GenericForeignKey field?
class User(models.Model):
...
limit = models.Q(app_label='your_app_label', model='brand') |
models.Q(app_label='your_app_label', model='creator') |
models.Q(app_label='your_app_label', model='agency')
content_type = models.ForeignKey(ContentType, limit_choices_to=limit, related_name='user_content_type')
object_id = models.UUIDField()
content_object = GenericForeignKey('content_type', 'object_id')
You can access the User's brand/creator/agency by using the following notation:
User.objects.get(pk=1).content_object
This will be an instance of Brand/Creator/Agency as per the content_type.
https://docs.djangoproject.com/en/stable/ref/contrib/contenttypes/#django.contrib.contenttypes.fields.GenericForeignKey
Update based on your comment
Re 1: Using email:
class User(models.Model):
...
email = models.EmailField(max_length=255, unique=True)
limit = models.Q(app_label='your_app_label', model='brand') |
models.Q(app_label='your_app_label', model='creator') |
models.Q(app_label='your_app_label', model='agency')
content_type = models.ForeignKey(ContentType, limit_choices_to=get_content_type_choices, related_name='user_content_type')
content_object = GenericForeignKey('content_type', 'email')
Note: Email can not be a nullable field anywhere if you follow this approach! Also this approach seems hacky/wrong since the email field is now declared in multiple places; and the value can change if you re-assign the content objects. It is much cleaner to link the GenericForeignKey using the discrete UUIDField on each of the other three models
Re 2: Using account_type field:
ContentType is expected to be a reference to a Django Model; therefore it requires choices that are Models and not integers.
The function of limit_choices_to is to perform a filtering such that all possible models are not surfaced as potential GenericForeignKey
class ContentType(models.Model):
app_label = models.CharField(max_length=100)
model = models.CharField(_('python model class name'), max_length=100)
objects = ContentTypeManager()
However, limit_choices_to does accept callables; so you can write a helper method that translates your account_type to the correct Model
I am not clear about how this transaltion should work; so I leave that to you.
Here is how I solve it, override the save method check your condition
tested on Django 3.2
CUSTOMER = [
('customer', 'customer'),
('supplier', 'supplier'),
]
class Customer(models.Model):
name = models.CharField(max_length=256, null=False, blank=False)
user_type = models.CharField(max_length=32, choices=CUSTOMER)
class SupplierOrder(models.Model):
price = models.FloatField(default=0)
supplier = models.ForeignKey(Customer, related_name='supplier', on_delete=models.CASCADE)
def save(self, *args, **kwargs):
supplier = get_object_or_404(Customer, id=self.supplier.id)
if supplier.user_type != 'supplier':
raise ValueError('selected user must be supplier')
super().save(*args, **kwargs)