I'm using django-tables and trying to display a table containing some related fields.
class User(models.Model):
name = models.CharField(_("name"), max_length=100)
comp = models.ForeignKey(Company)
class Company(models.Model):
name = models.CharField(_("name"), max_length=100)
country = models.CharField(_("country"), max_length=200)
def __unicode__(self):
return self.name
class UserTable(tables.Table):
class Meta:
model = User
fields = ('name', 'comp', 'comp.country',)
empty_text = _('No user')
I get the correct data but Comp in each related heading
+------+----------------+-----------------+
| Name | Comp | Comp |
+------+----------------+-----------------+
| Bob | Comp 1 | France |
| John | Comp 2 | United Kingdom |
| ... | ... | ... |
+------+----------------+-----------------+
What could be the reason ?
Shouldn't I get Name, Comp, Country ?
Update
Mistake on the original question, I have updated it.
Using accessor to resolve your fields allows to define the verbose names
class UserTable(tables.Table):
name = tables.Column(accessor='name', verbose_name=_('name'))
comp = tables.Column(accessor='comp', verbose_name=_('company'))
country = tables.Column(accessor='comp.country', verbose_name=_('country'))
From django-table docs -
fields – specify model fields to include
But you're including relationships -
fields = ('user', 'user.pref.country', 'user.pref.phone',)
I never used that app, so I'm not sure how it's working, but I think it's taking the verbose name of each field, in later two cases, user field comes first, hence it's taking user fields' verbose name.
Update:
It seems you can provide custom verbose names, try this. Not sure if this will work, as Country is a related field. -
class UserTable(tables.Table):
country = tables.Column(verbose_name="Country")
class Meta:
model = User
fields = ('name', 'comp', 'comp.country',)
empty_text = _('No user')
Related
I'm pretty new to django and the admin module. I'm looking for a way to add on a admin class
some fields that i query through a reverse relationship.
I can currently retrieve the interesting fields and put them in one column thanks to a specific function
using list_diplay, but i cannot manage to create a list_display field BY returned query object:
as example, now I get as column:
|Inventory_id| Mousqueton1 |
| 22 | foo1,foo2 |
and i would like to have this kind of output, to easily create filters:
|Inventory_id| Mousqueton1 | Mousqueton2 |
| 22 | foo1 | foo2 |
Here's my current models.py
class Kit(models.Model):
inventory_id = models.CharField(max_length=20,unique=True)
description = models.TextField(null=True)
creation_date = models.DateField(auto_now_add=True)
last_update = models.DateTimeField(auto_now=True)
class Mousquetons(models.Model):
inventory_id = models.CharField(max_length=20,unique=True)
serial = models.IntegerField(unique=False)
description = models.TextField(null=True)
creation_date = models.DateField(auto_now_add=True)
last_update = models.DateTimeField(auto_now=True)
kit = models.ForeignKey(Kit,on_delete=models.PROTECT,null=True)
and admin.py
#admin.register(Kit)
class KitAdmin(admin.ModelAdmin):
list_display= ['inventory_id','m']
def get_queryset(self, obj):
qs = super(KitAdmin, self).get_queryset(obj)
return qs.prefetch_related('mousquetons_set')
def m(self, obj):
return list(obj.mousquetons_set.all())
Maybe my data modeling is not the right way to perform this kind of operation, Any advice would be great.
Thanks !
I used python manage.py inspectdb > models.py to create models from my old SQL database. Workout.user_id is a foreign key to users table, but inspectdb lost this linking by making it just models.IntegerField().
Database looks like this:
Users
+----+------------+
| id | name |
+----+------------+
| 1 | Bob |
| 2 | Alice |
| 3 | Tom |
+----+------------+
Workout
+---------+------------+------------+
| user_id | workout_id | date |
+---------+------------+------------+
| 2 | 1 | 2021-02-18 |
| 2 | 2 | 2021-02-20 |
| 3 | 3 | 2021-02-21 |
+---------+------------+------------+
And models.py comes out like this:
# This is an auto-generated Django model module.
# You'll have to do the following manually to clean this up:
# * Rearrange models' order
# * Make sure each model has one field with primary_key=True
# * Make sure each ForeignKey and OneToOneField has `on_delete` set to the desired behavior
# * Remove `managed = False` lines if you wish to allow Django to create, modify, and delete the table
# Feel free to rename the models, but don't rename db_table values or field names.
from django.db import models
class Users(models.Model):
id = models.AutoField(primary_key=True)
name = models.TextField(blank=True, null=True)
class Meta:
managed = False
db_table = 'users'
class Workout(models.Model):
user_id = models.IntegerField()
workout_id = models.AutoField(primary_key=True)
date = models.DateField(blank=True, null=True)
class Meta:
managed = False
db_table = 'workout'
Workout.user_id is a foreign key to users table, but inspectdb lost this linking by making it just models.IntegerField().
I tried setting ForeignKey like this:
user_id = models.ForeignKey('Users', on_delete=models.CASCADE)
but it gives an error:
django.db.utils.OperationalError: (1054, "Unknown column 'workout.user_id_id' in 'field list'")
Can this kind of ForeignKey linking be achieved or how should I proceed?
Django automatically adds _id to the ForeignKey for the name of the table column, you thus name it user. You can also specify with the db_column=… parameter [Django-doc] the name of the database column, although that is not necessary here:
class Workout(models.Model):
user = models.ForeignKey(
'Users',
db_column='user_id'
)
workout_id = models.AutoField(primary_key=True)
date = models.DateField(blank=True, null=True)
class Meta:
managed = False
db_table = 'workout'
I'm trying to implement a keyword search like so:
keyword = request.GET['keyword']
books = Book.objects.all()
books = books.filter(
Q(title__contains=keyword) |
Q(summary__contains=keyword) |
Q(author__name__contains=keyword) |
Q(author__biography__contains=keyword) |
)
With the following models:
class Book(models.Model):
id = models.AutoField(primary_key=True)
title = models.CharField()
summary = models.CharField()
author = models.ManyToManyField('Author', through='BookToAuthor')
class BookToAuthor(models.Model):
id = models.AutoField(primary_key=True)
book_id = models.ForeignKey('Book')
author_id = models.ForeignKey('Author')
class Author(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField()
biography = models.CharField()
Everything works as expected, but the query execution alone is taking a significant amount of time (10-15 seconds). Through trial and error, I've been able to determine that this is caused by the ManyToMany Q() fields. I know I can use prefetch_related in order to cache/join the relational fields in Python and improve performance. But I'm having trouble getting this to work in combination with the (Q() | Q() ...) filtering.
Any suggestions on how to incorporate prefetch_related? Or an alternative method that would improve performance?
I'm totally new to django, coming from a PHP perspective. I want to write a real basic application with four classes: Book, Ebook, Genre and price. Each Book and Ebook should have one genre and many prizes. In a SQL-DB I'd put a field in Book and Ebook referencing to a genre table by id and a new table called something like Book_prices which is linking Books and Ebooks to prices.
table book_prices
id | type | price
---+--------+------
1 | book | 3
2 | book | 3
3 | ebook | 1
table book/ebook
id | ... | genre_id
---+-----+---------
1 | | 5
2 | | 7
3 | | 9
Basically I want to add a list of prices and ONE genre for each Ebook and Book. How can I do this using the django model? I know of model.ForeignKey() which could be applied to each Book/Ebook referencing to a genre. But what about my prices? If I add a ForeignKey() to a price it can only reference to a Book OR Ebook.
class Book:
name = models.CharField(max_length=100)
pages = models.IntegerField()
class Ebook:
name = models.CharField(max_length=100)
filesize = models.FloatField()
class Genre:
name = models.CharField(max_length=100)
info = models.TextField()
class Price:
currency = models.CharField(max_length=4)
amount = models.FloatField()
Here's one way. It uses inheritance to reduce duplication between classes.
It uses the contenttypes framework.
Also your classes need to subclass django.db.models
from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic
class Genre(models.Model):
name = models.CharField(max_length=100)
info = models.TextField()
class Price(models.Model):
currency = models.CharField(max_length=4)
amount = models.FloatField()
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
book = generic.GenericForeignKey('content_type', 'object_id')
class BookBase(models.Model):
name = models.CharField(max_length=100)
genre = models.ForeignKey(Genre)
class Meta:
abstract = True
class Book(BookBase):
pages = models.IntegerField()
class Ebook(models.Model):
filesize = models.FloatField()
class Genre:
name = models.CharField(max_length=100)
info = models.TextField()
class Price:
currency = models.CharField(max_length=4)
amount = models.FloatField()
class Book:
genre = models.ForeignKey(Genre)
name = models.CharField(max_length=100)
pages = models.IntegerField()
prices = models.ManyToManyField(Price)
class Ebook:
genre = models.ForeignKey(Genre)
name = models.CharField(max_length=100)
filesize = models.FloatField()
prices = models.ManyToManyField(Price)
Get all prices for a Book instance.
b1 = Book.objects.get(id=1)
prices = b1.prices.all()
ManyToManyField creates an intermediate table behind the scenes, exactly like you
would have done, if you did it manually. You can even define explicitly this table,
in case you want to add more fields to it, with the through parameter
Also consider using DecimalField for prices.
I want to have a model with a ManyToMany relationship with itself, I don't know how to write this but I'l try to write some code to illustrate what I want to do.
class Person(models.Model):
name = models.CharField()
occupation = models.CharField()
friends = models.ManyToManyField('self', through = PersonFriends)
My Model that I want the friends to go through
class PersonFriends(models.Model)
???
comment = models.CharField()
In a ManyToMany field with through relationship if the other model's name was "Pet" for example I'd name my fields in that through class person and pet and make them models. ForeignKey(Person) and Pet for example
What to I name my fields in my PersonFriends model for the two person-fields now that they are the same model?
You can do something like this:
class Person(models.Model):
name = models.CharField(max_length = 255)
occupation = models.CharField(max_length = 255)
friends = models.ManyToManyField('self', through = 'PersonFriends',
symmetrical = False)
# ^^^^^^^^^^^
# This has to be false when using `through` models. Or else your
# model will not validate.
class PersonFriends(models.Model):
source = models.ForeignKey(Person, related_name = 'source')
# ^^^^^^^^^^^^
# You need different `related_name` for each when you have
# multiple foreign keys to the same table.
target = models.ForeignKey(Person, related_name = 'target')
comment = models.CharField(max_length = 255)
Everything is described in the official docs for ManyToManyField.through_fields (you can search for 'recursive relationships' phrase there to quickly find what you need):
for django 1.11 you have to specify through and (!) through_fields arguments:
class Person(models.Model):
name = models.CharField(max_length=50)
# note the additional arguments here
friends = models.ManyToManyField(
'self',
# recursive relationships to self with intermediary
# through model are always defined as non-symmetrical
symmetrical=False,
through='PersonFriend',
# this argument is required to define a custom
# through model for many to many relationship to self
# position matters: 1 - source (from), 2 - target (to)
through_fields=('person', 'friend'),
)
class PersonFriend(models.Model):
# required relationship-defining foreign keys
# (note that the order does not matter, it matters
# in 'through_fields' argument in 'friends' field of the 'Person' model)
person = models.ForeignKey(Person, on_delete=models.CASCADE)
friend = models.ForeignKey(Person, on_delete=models.CASCADE)
# additional fields
comment = models.CharField()
Without assuming that friendships are symmetrical. Because Buzz Lightyear might be Woody's friend, but Woody isn't friends with Buzz Lightyear till near the end of the film. You can simplify both models and still have reasonable lookup names. You would of course need to make sure that you define two PersonFriends if it's a good friendship.
class Person(models.Model):
name = models.CharField()
occupation = models.CharField()
class PersonFriends(models.Model):
from_person = models.ForeignKey(Person, related_name='friends_with')
to_person = models.ForeignKey(Person, related_name='friends')
comment = models.CharField()
class Meta:
unique_together = ('from_person', 'to_person')
This has the added bonus of a comment for each direction of the friendship. i.e. Tyrion thinks Sansa is a lovely and intelligent, but lost girl. Whereas Sansa might think that Tyrion is an ugly but clever and kind-hearted kinda guy.
class PersonFriends(models.Model):
from_person = models.ForeignKey(Person, related_name='from_person')
to_person = models.ForeignKey(Person, related_name='to_person')
this is from db table structure of a ManyToMany relation to self from my Model structure. Django defines it like that..