No Values in ManyToManyField - python

I have a UserProfile where the User chose a language. Users should have the possibility to chose more than one language so i tried to use a ManyToManyField like so:
class Choices(models.Model):
languages = models.CharField(choices=settings.LANGUAGES, max_length=3)
def __unicode__(self):
return self.languages
class UserProfile(models.Model):
user = models.OneToOneField(User, null=True)
language = models.ManyToManyField(Choices)
in my form.py i did:
class UserProfileForm(forms.ModelForm):
language = forms.MultipleChoiceField(required=False, choices=settings.LANGUAGES)
class Meta:
model = UserProfile
fields=[
'Pictures',
.....
]
The problem is: The choices are shown in the template but in the admin the ManyToManyField is empty. So the Form works but the Model does not. Can somebody tell me why no Options are displayed? Where is the mistake at the M2M?

Related

Sort a displayed column defined by a custom model method in the Django admin interface

I want to be able to sort a table column defined using a custom method in the Django admin.
I narrowed down the problem to this simple example in Django:
models.py:
from django.db import models
class MyObject(models.Model):
name = models.CharField(_("name"), max_length=255)
layers = models.URLField(_("Layers"), blank=True, max_length=1024)
choices = models.TextField(
verbose_name=_("Choice values"),
blank=True,
help_text=_("Enter your choice"),
)
class Meta:
verbose_name = _("Object config")
verbose_name_plural = _("Objects config")
def __str__(self): # my custom method
return self.name
and admin.py:
from django import forms
from django.contrib import admin
class MyObjectAdminForm(forms.ModelForm):
"""Form"""
class Meta:
model = models.MyObject
fields = "__all__"
help_texts = {
"layers": "URL for the layers",
}
class MyObjectAdmin(admin.ModelAdmin):
form = MyObjectAdminForm
list_filter = ["name",]
search_fields = ["name",]
# I want the first column (__str__) to be sortable in the admin interface:
list_display = ["__str__", ...] # the ... represent some other DB fields
but for the moment I cannot sort that first column (it is grayed out, I cannot click on its title):
So how could I sort the first column in this admin table as defined by the __str__() method of the MyObject model? (please note that I cannot change the model itself. I'm also brand new to Django, so don't hesitate to detail your answer as if you were speaking to a kid.)

How can I include in the JSON's GET request the list of foreign keys of a model in Django?

My models have users that can have multiple devices. When I do a GET request on users it returns only the fields specified in the user model, as it should. But I want the option to include in the JSON returned by the GET request the list of devices the user has. How can I do that? Secondly, is there a way I can sometimes get a user with the list of devices in the same JSON, and other times without it? Also, I am really new to Django, and I would appreciate a lot code examples to understand better, if possible.
These are my models:
class User(models.Model):
name = models.CharField(max_length=100)
birth_date = models.DateField()
address = models.CharField(max_length=200)
class Device(models.Model):
description = models.CharField(max_length=200)
location = models.CharField(max_length=200)
max_energy_consumption = models.FloatField()
avg_energy_consuumption = models.FloatField()
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
My serializers:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = '__all__'
class DeviceSerializer(serializers.ModelSerializer):
class Meta:
model = Device
fields = '__all__'
And the following default ModelViewSets for CRUD api calls:
class UserViewSet(ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
class DeviceViewSet(ModelViewSet):
queryset = Device.objects.all()
serializer_class = DeviceSerializer
There are some different ways easiest one would be add a property in your User model and add that to your serializer
class User(models.Model):
name = models.CharField(max_length=100)
birth_date = models.DateField()
address = models.CharField(max_length=200)
#property
def devices(self):
return Device.objects.filter(user_id=self.id).values("location", "description").distinct()
class UserSerializer(serializers.ModelSerializer):
devices = serializers.ReadOnlyField()
class Meta:
model = User
fields = '__all__'
EDIT - for second part of your question:
I have experienced that writing '__all__' in our serializers not the best thing to do when we do not need all the information all the time(performance issues). To address this obsticle making a seperate serializer would be again an easy solution. Whenever I am facing this kind of thing i query same endpoint but send a different qs that i dont use in other endpoint in your case lets say your user viewsets route is something like /api/user/ you can add a qs when you send your get request to your backend and add ?with_devices=true.
Then you can use your second user serializer like this:
class UserViewSet(ModelViewSet):
queryset = User.objects.all()
def get_serializer_class(self):
if self.request.GET.get("with_devices", False):
return UserWithDeviceSerializer
return UserSerializer
where your serializers would be something like:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ["name", "birth_date", ..so on]
class UserWithDeviceSerializer(serializers.ModelSerializer):
devices = serializers.ReadOnlyField()
class Meta:
model = User
fields = '__all__'
This would give you what you asked in the comment.

Show InlineModelAdmin for nested Model

I have nested models with OneToOneFields and want to have an InlineModelAdmin form in one ModelAdmin to point to a nested model...
models.py:
class User(models.Model):
username = models.CharField(max_length=128)
password = models.charField(max_length=128)
class IdentityProof(models.Model):
user = models.OneToOneField(User, on_delete=models.PROTECT, related_name='proof')
proof_identity = models.FileField(upload_to='uploads/%Y/%m/%d/')
class Company(models.Model):
user = models.OneToOneField(User, on_delete=models.PROTECT, related_name='company')
name = models.CharField(max_length=128)
class Person(models.Model):
user = models.OneToOneField(User, on_delete=models.PROTECT, related_name='person')
name = models.CharField(max_length=128)
admin.py:
class IdentityProofInline(admin.TabularInline):
model = IdentityProof
#admin.register(Company)
class CompanyAdmin(admin.ModelAdmin):
inlines = [IdentityProofInline]
#admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
inlines = [IdentityProofInline]
For CompanyAdmin or PersonAdmin, I want to show its User's IdentityProof. How can I do that ?
I tried to use fk_name = 'user_proof or other combinations but it doesn't work...
Thanks.
This is not possible without an external package.
See Django Nested Inline and Django Nested Admin

Sorting of Users Dropdown list (ForeignKey) in Django Admin

I'm using the django admin and want to sort (by last_name) the dropdown list of users in a related field (ForeignKey).
I'm am using the standard User model in django. I tried the following in the model.py which is not working:
...
from django.contrib.auth.models import User
class Meta:
ordering = ['last_name']
User.add_to_class("Meta", Meta)
...
class Application(models.Model):
...
user = models.ForeignKey(User,
verbose_name="xyz",
null=True, blank=True,
limit_choices_to={'is_active': True},
on_delete=models.PROTECT)
...
Why is this not working? Is there another (easy) way to do it? I probably should have gone for a custom user model. But I didn't do that and changing it now is seams a lot of work.
I am using django 2.0.5 with python 3.6.5
Any help is appreciated.
Why not do it in your model class
class MyModel (models.Model):
user = models.ForeginKey(User)
...
class Meta:
ordering ['user__last_name']
It seams that the ordering in the User model is overwritten by the ordering specified in UserAdmin. Specifying my own UserAdmin solved the problem.
class MyUserAdmin(UserAdmin):
...
ordering = ["last_name", "first_name"]
...
admin.site.register(User, MyUserAdmin)

Overriding formset in TabularInline Django admin form

I'm having trouble overriding the formset on a TabularInline inline of a ModelAdmin object in my admin site. I know you're supposed to have a model associated with a TabularInline object, but I'm not sure how to specify this on the form object used to generate the formset. With the code below, I'm getting "'AppAssetInline.formset' does not inherit from BaseModelFormSet."
class AppAssetForm(forms.ModelForm):
model = App.assets.through
primary = forms.BooleanField()
uuid = forms.CharField()
class AppAssetInline(admin.TabularInline):
model = App.assets.through
AssetFormset = formset_factory(AppAssetForm)
formset = AssetFormset
class AppAdmin(admin.ModelAdmin):
inlines = [AppAssetInline,]
The answer to my question didn't have to do with how I was structuring my forms, but rather how I was joining fields on my models. I had the following structure in my models:
class App(models.Model):
package = models.FileField(upload_to=settings.APP_PACKAGE_ROOT)
assets = models.ManyToManyField('AppAsset', blank=True, null=True)
download_count = models.IntegerField(default=0)
class AppAsset(models.Model):
def __unicode__(self):
return self.asset_file.name
notes = models.CharField(max_length=255, null=True, blank=True)
type = models.CharField(max_length=255, null=True, blank=True)
asset_file = models.FileField(upload_to=settings.APP_PACKAGE_ROOT)
What I did was change the structure such that AppAsset now has a foreign key on App for its assets. After that, I could use the TabularInline on the AppAsset model with no problems. Here are the latest source files:
https://github.com/ridecharge/spout/blob/master/Spout/AppDistribution/models.py
https://github.com/ridecharge/spout/blob/master/Spout/AppDistribution/admin.py
You should use django.forms.models.inlineformset_factory instead of formset_factory

Categories

Resources