Django admin page autocomplete on reverse ForeignKey - django

Disclamer:
This is an edit question because I got some answers until now.
I would like to create an autocomplete field in InlineModelAdmin, with similar behavior as autocomplete_fields. I know, Django natively supports this only on ForeignKey, but I need it on the reverse side.
Is there a way - or a library that could do this?
I look into :
https://django-autocomplete-light.readthedocs.io/en/master/tutorial.html
https://django-grappelli.readthedocs.io/en/latest/customization.html
https://django-admin-autocomplete-all.readthedocs.io/en/latest/readme.html
But I didn't found this functionality...
Past question code:
I have models like this:
Models
class Customer(models.Model):
customer_ref = models.CharField(unique=True, max_length=50)
name = models.CharField(null=False, max_length=80)
country = models.CharField(null=True, max_length=50)
class Assortment(models.Model):
name = models.CharField(null=False, max_length=50)
customers = models.ManyToManyField(Customer, related_name='assortments', blank=True)
products = models.ManyToManyField(Product, related_name='assortments', blank=True)
class Subsidiary(models.Model):
subsidiary_ref = models.CharField(unique=True, max_length=50)
name = models.CharField(null=False, max_length=80)
address = models.TextField(null=True)
city = models.CharField(null=True, max_length=50)
coordinates_x = models.DecimalField(null=True, decimal_places=2, max_digits=6)
coordinates_y = models.DecimalField(null=True, decimal_places=2, max_digits=6)
phone_number = models.CharField(null=True, max_length=50)
frequency = models.ForeignKey(Frequency, on_delete=models.SET_NULL, null=True)
channel = models.CharField(null=True, blank=True, max_length=50)
subchannel = models.CharField(null=True, blank=True, max_length=50)
user = models.ForeignKey(User, related_name='subsidiaries', on_delete=models.SET_NULL, null=True)
day_planned = models.BooleanField(default=False)
customer = models.ForeignKey(Customer, on_delete=models.CASCADE, related_name='subsidiaries')
class Asset(models.Model):
identification = models.CharField(unique=True, max_length=50)
serial = models.CharField(max_length=50)
name = models.CharField(null=True, max_length=50)
subsidiary = models.ForeignKey(Subsidiary, related_name='assets', null=True, blank=True, on_delete=models.DO_NOTHING)
Admin
#admin.register(Customer)
class CustomerAdmin(admin.ModelAdmin):
list_display = ['customer_ref', 'name', 'country']
list_filter = ['country']
autocomplete_fields = ['assortments']
#admin.register(Subsidiary)
class SubsidiaryAdmin(admin.ModelAdmin):
exclude = ['day_planned']
list_display = ['subsidiary_ref', 'customer', 'name', 'address', 'phone_number', 'frequency', 'user']
list_editable = ['frequency', 'user']
list_filter = ['frequency', 'user']
search_fields = ['subsidiary_ref', 'customer__name', 'name']
autocomplete_fields = ['assets']
CustomerAdmin is 'working' without error but field 'assortments' is not visible.
SubsidiaryAdmin throws error :
<class 'mobileapp.admin.SubsidiaryAdmin'>: (admin.E038) The value of 'autocomplete_fields[0]' must be a foreign key or a many-to-many field.
witch is weird because I don't see any difference from the first example.
How to fix this?

assortments is not visible in the list page as you have set list_display to
list_display = ['customer_ref', 'name', 'country']
Since assortments is a many-to-many relationship, you have to write a custom ModelAdmin to display them.
As mentioned in the django docs, autocomplete_fields works only for FK & M2M fields of that model. In the AssetAdmin, you can set autocomplete for Subsidiary field.
#admin.register(Asset)
class AssetAdmin(admin.ModelAdmin):
autocomplete_fields = ['subsidiary']

Related

How to inner join multi tables or merge multi serializers in Django?

i have this code here for the models:
class Users(models.Model):
first_name = models.CharField(max_length=32, blank=True, null=True)
last_name = models.CharField(max_length=32, blank=True, null=True)
email = models.EmailField(max_length=254, unique=True)
class Images(models.Model):
user= models.ForeignKey(Users, on_delete=models.RESTRICT)
encoded_pic = models.JSONField(
encoder=None, default=dict, blank=True, null=True)
pic_thumbnail_path = models.CharField(max_length=222, blank=True, null=True)
class WorkingDays(models.Model):
user= models.ForeignKey(Users, on_delete=models.RESTRICT)
day = models.DateField(blank=True, null=True)
enter_time = models.DateTimeField(blank=True, null=True)
exit_time = models.CharField(max_length=32, blank=True, null=True)
class Departments(models.Model):
user= models.ForeignKey(Users, on_delete=models.RESTRICT)
department_name = models.CharField(max_length=32, blank=True, null=True)
These are the serializers:
class UserssSerializer(serializers.ModelSerializer):
class Meta:
model = Users
fields = '__all__'
class ImagesSerializer(serializers.ModelSerializer):
class Meta:
model = Images
fields = '__all__'
class WorkingDaysSerializer(serializers.ModelSerializer):
class Meta:
model = WorkingDays
fields = '__all__'
class DepartmentsSerializer(serializers.ModelSerializer):
class Meta:
model = WorkingDays
fields = '__all__'
I tried this code but it only returns the images fields not the others
data=Images.objects.filter(user_id__workingdays=pk).values()
What i want to do is to inner join images, workingdays and departments table using user_id field
also is there away to merge serializers of all these 3 tables to make the serializer returns all the fields from all the 3 tables?

Serializing specific fields from another model

I was trying to serialize policy_length, coverage_amount from Policy model and use it in a serializer that was using Insuree model.
I'm using Policy and Insuree models:
Policy model
class Policy(TimeStampedModel):
policy_length = models.FloatField(max_length=2, blank=True, null=True)
coverage_amount = models.FloatField(max_length=256, blank=True, null=True)
Insuree model
class Insuree(TimeStampedModel):
user = models.OneToOneField(
'User', related_name='insuree', null=False, on_delete=models.CASCADE,
primary_key=True)
coverage_amount = models.ForeignKey(Policy, on_delete=models.CASCADE, max_length=256, blank=True, null=True)
policy_length = models.ForeignKey(Policy, on_delete=models.CASCADE, max_length=2, blank=True, null=True)
class PolicySelectionSerializer(serializers.ModelSerializer):
class Meta:
model = Insuree
fields = ('age', 'gender', 'disposable_income') #Current fields
fields = ('age','gender',disposable_income', 'policy_length', 'coverage_amount') #The field I need and want to do
You may use Nested Serializer.
PolicySerializer(ModelSerializer)
class Meta:
model = Policy
fields = ( 'policy_length', 'coverage_amount')
InsureeSerializer(ModelSerializer)
class Meta:
policy = PolicySerializer(many = True)
model = Insuree
fields = ('age', 'gender', 'disposable_income', 'policy',)

Django api filter search on other models without a direct foreign field

I have two models named user, skill, and profile.
I am trying to implement a search filter on the user's skills. which means when someone searches for something that is contained in the skills of a user, that user would appear in the search result.
Note: when the user signs up, a signal is used to auto-create a profile for that user. The user simply updates their profile to add skills and other things.
user model
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(max_length=254, unique=True)
name = models.CharField(max_length=250)
picture = models.TextField(null=True, blank=True)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
last_login = models.DateTimeField(null=True, blank=True)
date_joined = models.DateTimeField(auto_now_add=True)
slug = models.SlugField(max_length=255, unique=True, blank=True)
profile model
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='profiles')
date_of_birth = models.DateField(blank=True, verbose_name="DOB", null=True)
bio = models.TextField(max_length=500, blank=True, null=True)
skills = models.ManyToManyField(Skill, related_name='skills')
sex = models.CharField(max_length=6, choices=SEX, blank=True, null=True)
type_of_body = models.CharField(max_length=8, choices=BODYTYPE, blank=True, null=True)
feet = models.PositiveIntegerField(blank=True, null=True)
inches = models.PositiveIntegerField(blank=True, null=True)
lives_in = models.CharField(max_length=50, blank=True, null=True)
updated_on = models.DateTimeField(auto_now=True)
skill model
class Skill(models.Model):
name = models.CharField(max_length=60)
subcategory = models.CharField(max_length=60, blank=True, null=True)
description = models.TextField(null=True, blank=True)
created_on = models.DateTimeField(auto_now=True)
updated_on = models.DateTimeField(auto_now_add=True)
updated_by = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, on_delete=models.DO_NOTHING)
the user view, where the search is done from
class ListUsersView(generics.ListAPIView):
'''
Gets all the users in the database
'''
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = [AllowAny]
filter_backends = [filtr.SearchFilter]
search_fields = ['email', 'name']
currently, the solution above works, but when I add to the search_fields other fields like profiles__skills in order to include results where there is a skill like that created by ay user, the code doesn't work.
Please, how can I get the skills in the profile of a user to show in the search?
The SearchFilter class supports simple single query parameter based searching. The search_fields attribute should be a list of names of text type fields on the model.
profiles__skills is not a field. You should use a text field eg. profiles__skills__name
class ListUsersView(generics.ListAPIView):
'''
Gets all the users in the database
'''
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = [AllowAny]
filter_backends = [filtr.SearchFilter]
search_fields = ['email', 'name', 'profiles__skills__name']

How to allow blank field on OneToOne serializer field for Django REST Framework API?

When POSTing to my API endpoint I get a field is required error on a OneToOne field, but I create the OneToOne Field in the save() method of the model I'm POSTing.
I've tried setting default=None, null=True, and blank=True
Device Model
class Device(models.Model):
name = models.CharField(max_length=50)
created = models.DateTimeField(auto_now_add=True, editable=False)
last_updated = models.DateTimeField(auto_now=True, editable=False)
brand = models.TextField(max_length=50)
year_purchased = models.IntegerField()
serial_number = models.TextField(max_length=100)
info = models.TextField(max_length=100)
qrcode = models.ImageField(upload_to='', blank=True, null=True)
def save(self, **kwargs):
super(Device, self).save(**kwargs)
if not self.qrcode:
self.generate_qrcode()
if not self.checkouts.exists():
checkout = Checkout(item=self)
checkout.save()
Checkout Model
class Checkout(models.Model):
status_choices = (...)
# Fields
slug = extension_fields.AutoSlugField(populate_from='item', blank=True)
created = models.DateTimeField(auto_now_add=True, editable=False)
last_updated = models.DateTimeField(auto_now=True, editable=False)
due_back = models.DateField(null=True, blank=True)
checked_out = models.DateTimeField(blank=True, null=True)
status = models.CharField(max_length=2, choices=status_choices, default=in_stock)
# Relationship Fields
user = models.OneToOneField(
DeviceUser,
on_delete=models.CASCADE,
related_name="checkouts",
blank=True,
null=True,
default=None,
)
item = models.OneToOneField(
Device,
on_delete=models.CASCADE,
related_name="checkouts",
primary_key=True,
blank=True,
default=None,
)
Device Serializer
class DeviceSerializer(serializers.ModelSerializer):
class Meta:
model = models.Device
fields = (
'pk',
'name',
'created',
'last_updated',
'brand',
'year_purchased',
'serial_number',
'info',
'checkouts',
)
When POSTing http POST http://localhost:8000/main/api/device/ brand=test2 info=123213123 name=test2 serial_number=12321321 year_purchased=12 'Authorization: Token .....'
I expect to get a confirmation that a device was created, instead I get
"checkouts": [ "This field is required." ]
Specify checkouts field to your DeviceSerializer serializer as,
checkouts = serializers.DateTimeField(default=None, source='checkouts.checked_out', read_only=True)
# code sample
class DeviceSerializer(serializers.ModelSerializer):
checkouts = serializers.DateTimeField(default=None, source='checkouts.checked_out', read_only=True)
class Meta:
model = models.Device
fields = (
'pk',
'name',
'created',
'last_updated',
'brand',
'year_purchased',
'serial_number',
'info',
'checkouts',
)

access to another model fields with foreign key in django admin

I have a contact us class in django model,that using "user_id" foreign key,in admin.py file,i want using userprofile data with user_id key,such as "name" filed,in userprofile model "user_id" is foreign_key too,how can do it?
in models.py :
class Ticket(models.Model):
user_id = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, editable=False, verbose_name="user")
replier_id = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, related_name="replier")
guest_name = models.CharField(max_length=50, blank=True, null=True, editable=False)
guest_email = models.CharField(max_length=255, blank=True, null=True, editable=False)
user_phone = models.CharField(max_length=13, blank=True, null=True, editable=False)
subject = models.CharField(max_length=255, blank=True, null=True, editable=False)
message = models.TextField(editable=False)
reply = models.TextField(blank=True, null=True)
date = models.DateTimeField(auto_now=True, editable=False)
reply_date = models.DateTimeField(blank=True, null=True)
user_ip = models.GenericIPAddressField(editable=False)
in admin.py
class ContactUsAdmin(admin.ModelAdmin):
actions = None
fields = ('user_name', 'user_email', 'subject', 'date', 'message', 'user_phone', 'reply')
list_display = ('user_name', 'user_email', 'subject', 'date', 'is_replied')
list_filter = ('date',)
search_fields = ['user_id__first_name', 'user_id__email', 'guest_name', 'guest_email', 'subject', 'message']
readonly_fields = ('user_name', 'user_email', 'subject', 'date', 'message', 'user_phone', 'reply_date')
i want show name from UserProfile class in admin.py fields
By the question title of your query. I am giving an incentive how to do that in django:
class Album(models.Model):
AlbumName = models.CharField(max_length=250)
AlbumSinger = models.CharField(max_length=250)
AlbumGenre = models.CharField(max_length=250)
AlbumImage = models.FileField(null=True)
SingerImage = models.FileField(null=True)
class Musics(models.Model):
album = models.ForeignKey(Album, related_name='music')
MusicName = models.CharField(max_length=250)
MusicLength = models.CharField(max_length=250)
MusicFile = models.FileField(null=True)
Using foreignkey album in Musics. you can access to all the music according to their album. And to access them in admin, just import class name and do the following:
admin.site.register(Album)
admin.site.register(Musics)
I hope it will clear your confusion.
finally i resolve it,when in userprofile model,user_id has "profile" related_name,in every where,with user_id foreign key we have all related field from every model that user_id is foreign key in it
you can see those with using dir("you user_id instance").
so,with user_id.profile.name i get the profile name of user,without any importing or change!