Django many to many admin with custom through object - python

lets assume that I have such a model:
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=128)
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
class Membership(models.Model):
person = models.ForeignKey(Person)
group = models.ForeignKey(Group)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)
class Meta:
db_table = mysql_membership
So my in-the-middle table is chosen (and manually created) by me. And now I would like to create the admin with inline formset in for Group objects. Just like this:
class MembershipInline(admin.TabularInline):
model = Membership
extra = 1
class GroupAdmin(admin.ModelAdmin):
inlines = (MembershipInline,)
admin.site.register(Group, GroupAdmin)
The problem is, that when I try to get edit form for a given Group, I get this error:
Exception Type: OperationalError
Exception Value:
(1054, "Unknown column 'mysql_membership.id' in 'field list'")
This is understandable to me because the primary key in this mysql_membership table is a set of columns (instead of one simple primary_key column):
PRIMARY KEY (person, group, invite_reason)
I want you to know, that I CAN'T edit the database schema. How can I enable formset for many-to-many field based on custom "through" table/model? Please help.
btw. I was using example from:
https://docs.djangoproject.com/en/1.6/ref/contrib/admin/#working-with-many-to-many-intermediary-models

Related

Django: Question regarding queries over a junction table/intermediary model

My question concerns the many-to-many section of the django models docs:
It is mentioned there that by using an intermediary model it is possible to query on the intermediary model's attributes like so:
Person.objects.filter(
group__name='The Beatles',
membership__date_joined__gt=date(1961,1,1))
However for the other many-to-many model (Group) a similar query results in a FieldError:
# which groups have a person name containing 'Paul'?
Group.objects.filter(person__name__contains="Paul")
Yet queries that reference the junction table explicity do work:
Person.objects.filter(membership__group__name__contains="The Beatles")
Group.objects.filter(membership__person__name__contains="Paul")
Shouldn't Group therefore have access to Person via the junction model?
Models:
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=128)
def __str__(self):
return self.name
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
def __str__(self):
return self.name
class Membership(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)
"The model that defines the ManyToManyField uses the attribute name of
that field itself, whereas the “reverse” model uses the lowercased
model name of the original model, plus '_set' (just like reverse
one-to-many relationships)." (docs: Many-to-many relationships)
So instead of
Group.objects.filter(person__name__contains="Paul")
the correct query is
Group.objects.filter(members__name__contains="Paul")
since the related model is accessible via the name of the field attribute (not the model).

django queryset filter check if ManyToMany Field contains specific object

I have these two models :
class Project(models.Model):
name = models.CharField(max_length=50)
users = models.ManyToManyField(User)
class User(models.Model):
name = models.CharField(max_length=25)
I want to get a queryset containing all the Projects with a specific user in the 'users' ManyToManyField
I tried this : Project.objects.filter(users__contains=user), but it's not working
Does someone know how can I do it ?
if filtering with id:
Project.objects.filter(users=search_id)
if filtering with user name:
Project.objects.filter(users__name__icontains=search_input)

Assigning current user in rest framework view

I have been getting my head around these basics but I am not getting it right. I am trying to associate my view to my user model using team which is a foreign key. When I try to create of a gps, I get an error saying "team is a required field" but instead it should be read only. The team attribute should be filled automatically with the id of the currentUser
Model
class User(models.Model):
first_name = models.CharField(max_length=200,blank=False)
last_name = models.CharField(max_length=200, blank=False)
class Gps(models.Model):
location = models.CharField(max_length=200,blank=False)
team= models.ForeignKey(User, on_delete=models.CASCADE)
serializers
class GpsSerializer(serializers.ModelSerializer):
class Meta:
model = Gps
fields = ('id','location','team')
view
class Gps_list(generics.ListCreateAPIView):
queryset = Gps.objects.all()
serializer_class = GpsSerializer
team = serializers.PrimaryKeyRelatedField(
read_only=True,
default=serializers.CurrentUserDefault()
)
There are two changes needed. First, team field definition should be moved to serializer class instead of view. Second, you should use Django's contrib.auth.User model instead of your definition of User, as because serializers.CurrentUserDefault() will bring request.user only. So you should remove your User definition and import that to your models.py:
from django.contrib.auth.models import User
Further steps would be to replace read_only=True with queryset=User.objects.all() to allow create.

Django - use same column for two foreign keys

I have three models:
class UserProfile:
user_id = OneToOneField(User, related_name='profile')
name = CharField
class User:
# standard django model
class Channel:
owner = ForeignKey(User)
And now, I want to get channels filtering on user name. So what I can do is:
Channel.objects.filter(owner__profile__name__icontains='foo')
But this joins User table and then joins UserProfile, which is not the best, because I would like to join UserProfile table on user_id instead (I would have one join instead of two)
I tried to add another foreign key to model like this:
class Channel:
owner = models.ForeignKey(
User,
db_column='owner_id',
)
owner_profile = models.ForeignKey(
UserProfile,
db_column='owner_id',
to_field='user_id')
But Django doesnt like it....
posts.Post: (models.E007) Field 'owner_profile' has column name 'owner_id' that is used by another field.
HINT: Specify a 'db_column' for the field.
Is there any clean workaround for this?
Django content type framework would do the trick
in your case:
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
class Channel(models.Model):
owner_ct = models.ForeignKey(to=ContentType, on_delete=models.CASCADE, blank=True, null=True) # foreign key table indicator (User,Profile) in your case
owner_content = models.CharField(max_length=64, blank=True, null=True) # uuid(foreign key) will be placed here as string
owner_object = GenericForeignKey('owner_ct', 'owner_content') # bridge to object
class User(models.Model):
channels = GenericRelation(to='Channel', object_id_field='owner_content', content_type_field='owner_ct',related_query_name='channels') # User.channels will be available
class Profile(models.Model):
channels = GenericRelation(to='Channel', object_id_field='owner_content', content_type_field='owner_ct',related_query_name='channels') # Profile.channels will be available

Using Custom User DB Structure in Mysql for Django 1.8

I have a question regarding the table structure for User if I am extending its functionalities, using a MySQL database.
Given the models.py file
class LibraryUser(models.Model):
user_id = models.OneToOneField(User)
is_catalogue_subscriber = models.IntegerField(default=1)
is_research_subscriber = models.IntegerField(default=1)
library_membership_number = models.CharField(max_length=64)
I have a table structure for SQL
CREATE TABLE library_user(
user_id int(10) primary key
is_catalogue_subscriber integer(1) DEFAULT 1
is_research_subscriber = integer(1) DEFAULT 1
library_membership_number = varchar(16)
)
So now, when I fire up my server and access the accounts in the admin page, Django is throwing an error:
Exception Type: OperationalError
Exception Value:
(1054, "Unknown column 'library_user.id' in 'where clause'")
Use
user = models.OneToOneField(User, primary_key=True)
i.e. drop the _id in the attribute name.
In case you simply want to define a richer user model (i.e. add more attributes) you can
use a one-to-one relationship to a model containing the fields
for additional information. This one-to-one model is often called a
profile model, as it might store non-auth related information about a
site user. For example you might create a LibraryUser model:
from django.contrib.auth.models import User
class LibraryUser(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
is_catalogue_subscriber = models.IntegerField(default=1)
is_research_subscriber = models.IntegerField(default=1)
library_membership_number = models.CharField(max_length=64)
Assuming an existing LibraryUser Fred Smith who has both a User and LibraryUser model, you can access the related information using Django’s standard related model conventions:
>>> u = User.objects.get(username='fsmith')
>>> freds_department = u.libraryuser.department
Then to add the profile model’s fields to the user page in the admin do
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User
from my_user_profile_app.models import LibraryUser
# Define an inline admin descriptor for Employee model
# which acts a bit like a singleton
class LibraryUserInline(admin.StackedInline):
model = LibraryUser
can_delete = False
verbose_name_plural = 'libraryuser'
# Define a new User admin
class UserAdmin(UserAdmin):
inlines = (LibraryUserInline, )
# Re-register UserAdmin
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
All taken from the official docs
What you are missing is that by default django models automatically include an id field that maps to models.AutoField().
You need to specify that your DB table id is user_id instead of id.
https://docs.djangoproject.com/en/1.8/topics/db/models/#automatic-primary-key-fields
You want to make your user_id the primary key. It should work by adding primary_key=True to that field, like:
class LibraryUser(models.Model):
user_id = models.OneToOneField(User, primary_key=True)
is_catalogue_subscriber = models.IntegerField(default=1)
is_research_subscriber = models.IntegerField(default=1)
library_membership_number = models.CharField(max_length=64)

Categories

Resources