default language value overrides translation field in admin - django

I am using Django model translation for the multi-linguistic site(English(default), Arabic, French), the problem is when saving data in django admin default overrides both Arabic and French translation fields. But in database actual value presents.
My Model:
plan/models.py
class operators(models.Model):
country = models.ForeignKey(Country, on_delete=models.CASCADE)
operator = models.CharField(max_length=100, null=True, blank=True)
image = models.ImageField(verbose_name=_('Operator Logo'), max_length=255,null=True,blank=True)
class Meta:
verbose_name = 'Operator'
verbose_name_plural = 'Operators'
db_table = 'operator'
def __str__(self):
return self.operator
plan/translation.py:
#register(operators)
class OperatorsTranslationOptions(TranslationOptions):
fields = ('operator',)
plan/admin.py:
class OperatorAdmin(admin.ModelAdmin):
list_display = ('operator', 'country')
.
.
.
admin.site.register(operators, OperatorAdmin)
settings.py:
INSTALLED_APPS = [
'modeltranslation',
.
.
.
'plan',
]
.
.
.
LANGUAGES = (
('en', gettext('English')),
('ar', gettext('Arabic')),
('fr', gettext('French')),
)
.
.
MODELTRANSLATION_TRANSLATION_FILES = (
'plan.translation',
)
MODELTRANSLATION_DEBUG = True
Below is the database value
planbaker=# select * from operator;
id | operator | image | country_id | operator_ar | operator_en | operator_fr
----+------------+-----------------------+------------+--------------+-------------+-------------
1 | airtel-ind | pb_Zu2y9BE.jpeg | 1 | | |
3 | aircel | 360_360_1_vsXSDEo.JPG | 2 | ايرتل | aircel |
2 | aircel-sa | 352_hQ4TZVq.jpeg | 2 | زين السعودية | aircel-sa | aircel-sa
But in Admin all the field have operator value only in english .
Can somebody please tell me where i went wrong? I tried both updte_translation_fields and sync_translation_fields but not helping

Try using modeltranslation.admin.TranslationAdmin instead of ModelAdmin for your OperatorAdmin class.
In order to be able to edit the translations via the django.contrib.admin application you need to register a special admin class for the translated models. The admin class must derive from modeltranslation.admin.TranslationAdmin which does some funky patching on all your models registered for translation.
https://django-modeltranslation.readthedocs.io/en/latest/admin.html

Related

Get latest payment from related model in Django Admin

I'd like to output a table using "Premises" model in Django admin. In addition I'd like include the output in this table of an additional column, say "last utility payment". It is actually a column in a related table. There may be no payment in the database so admin should able to see either an empty cell or the date of payment.
I was able to write a DB query that displays the information I need. Its significant and worked part is given below:
SELECT jp.id,
jp.number apartment,
jp.building_number building,
jp.rent,
jp.arrears,
jpm.last_payment
FROM jasmin_premises jp
LEFT JOIN (
SELECT pm.premises_id,
max(pm.paid) last_payment
FROM jasmin_payment pm
GROUP BY pm.premises_id
) jpm ON jp.id = jpm.premises_id;
And the output is similar to the following:
id | apartment | building | rent | arrears | last_payment
--------------------------------------------------------------
170 | 1X | 6-A | 297.43 | 2.57, | NULL
72 | 2 | 4 | 289.66 | -678.38 | 2021-01-31
173 | 3Z | 7 | 432.86 | 515.72 | 2021-02-04
73 | 4 | 8-B | 292.25 | 515.44 | 2021-02-04
74 | 5 | 8-B | 112.42 | 3249.34 | NULL
75 | 6A | 122 | 328.48 | 386.23 | 2021-02-04
76 | 7 | 42 | 482.06 | 964.12 | 2021-01-31
77 | 8 | 1 | 433.71 | 867.42 | 2021-01-31
78 | 9C | 12 | 322.79 | 322.79 | 2021-02-04
79 | 10 | 122 | 324.22 | 0 | 2021-02-04
80 | 12 | 12 | 322.79 | 1232.46 | NULL
81 | 14 | 5-Z | 440.82 | 978.44 | 2021-02-04
And I'm using the following models (only a significant part):
class Premises(models.Model):
number = models.CharField(
blank=False,
null=False,
max_length=10)
building_number = models.CharField(
blank=False,
null=False,
max_length=3)
rent = models.DecimalField(
blank=False,
null=False,
max_digits=12,
decimal_places=2,
default=0.0)
area = models.DecimalField(
blank=False,
null=False,
max_digits=5,
decimal_places=2)
class Payment(models.Model):
paid = models.DateField(
blank=False,
null=False)
premises = models.ForeignKey(
Premises,
blank=False,
null=False,
on_delete=models.CASCADE,
related_name='payments',
db_index=True)
Is there a way to override admin.ModelAdmin.get_queryset (for example using annotations) to get an extra column like in my example above? Is there any other way to make a LEFT JOIN on a compound DB query using Django ORM?
to make this query in django you have to add the models.Manager() to the tables like this:
models.py
class Premises(models.Model):
# existent code
objects = models.Manager()
class Payment(models.Model):
# existent code
objects = models.Manager()
In the part of app you want to access this information
from .models import Premises, Payment
premises = Premises.objects.all()
data_to_display = []
for premise in premises:
payments = Payment.objects.filter(premises=premise).order_by('-paid')
if len(payments) == 0:
last_payment = "Null"
else:
last_payment = payments[0]
object_to_list = {
"id": premise.id,
"apartment": premise.number,
"building": premise.building_number,
"rent": premise.rent,
"arreaars": premise.area,
"last_payment": last_payment.paid
}
data_to_display.append(object_to_list)
The solution is to add an explicit subquery to a QuerySet using the Subquery expression. We'll need also to use OuterRef because a queryset in a Subquery needs to refer to a field from the outer query.
So let's create a subquery:
from django.db.models import OuterRef
payments = Payment.objects.filter(
premises=OuterRef('pk')
).order_by('-paid')
The next step is to pass payments subquery to a queryset:
from django.db.models import Subquery
# 'payments' here is from example above
premises = Premises.objects.annotate(
last_payment=Subquery(payments.values('paid')[:1])
)
Finally, lets see the used SQL to query objects rows the database:
print(premises.query)
(the output is formatted, only the significant part is shown)
SELECT "jasmin_premises"."id",
"jasmin_premises"."number",
"jasmin_premises"."building_number",
"jasmin_premises"."arrears",
"jasmin_premises"."rent",
(SELECT U0."paid"
FROM "jasmin_payment" U0
WHERE U0."premises_id" = "jasmin_premises"."id"
ORDER BY U0."paid" DESC
LIMIT 1) AS "last_payment"
FROM "jasmin_premises";
Now, after performing the tests, we can use this in our ModelAdmin:
from django.contrib import admin
from django.db.models import OuterRef, Subquery
from .models import Payment, Premises
#admin.register(Premises)
class PremisesAdmin(admin.ModelAdmin):
list_display = (
'number',
'building_number',
'rent',
'arrears',
'last_payment',
)
def get_queryset(self, request):
qs = super().get_queryset(request)
payments = Payment.objects.filter(
premises=OuterRef('pk')
).order_by('-paid')
qs = qs.annotate(
last_payment=Subquery(payments.values('paid')[:1]),
)
return qs
def last_payment(self, obj):
return obj.last_payment
last_payment.short_description = 'Last payment'
last_payment.admin_order_field = 'last_payment'
Well, this doesn't use JOINs, but this approach will force Django to execute a subquery.
Probably in some cases it may be possible to write an equivalent queryset that performs the same task more clearly or efficiently however, this is the best I have achieved so far.

Linking one table to each user in Django

I am working on a Django project where I need to link one table(model) to each user.
Assume MyTable_1 maps to user_1 and so on.
The primary key for MyTable will be a DateField which contains continuous dates from the time user signed-up.
MyTable_1 for User_1
|-----------|----------|-------------|-----------------|
| Date(PK) | food_ate | game_played | ran_today |
|-----------|----------|-------------|-----------------|
| 10/01/20 | rice | chess | Yes |
|-----------|----------|-------------|-----------------|
| 11/01/20 |sandwhich | tennis | No |
|-----------|----------|-------------|-----------------|
MyTable_2 for User_2
|-----------|----------|-------------|-----------------|
| Date(PK) | food_ate | game_played | ran_today |
|-----------|----------|-------------|-----------------|
| 16/03/19 | pizza | rugby | Yes |
|-----------|----------|-------------|-----------------|
| 17/03/19 | pasta | football | Yes |
|-----------|----------|-------------|-----------------|
And so on for every new user created. User logs in those information in MyTable.
How can I implement this? I am using PostgreSQL and have written custom User Model.
You really don't need seperate tables just seperate rows.
A ForeignKey relation will do the trick, something like this in your models.py:
# user model:
User(models.Model, ...):
first_name = models.CharField(...)
last_name = models.CharField(...)
...
# log model:
Log(models.Model):
user = models.ForeignKey(User, ...)
date = models.DateField(...)
food_ate = models.CharField(...)
game_played = models.CharField(...)
ran_today = models.CharField(...)
class Meta: unique_together = ('user', 'date',)
Then, elsewhere, you can access your users' logs like so:
user = User.objects.get(id='the_user_id')
logs = user.logs.all()

Django query assigns database columns to wrong model instance properties

Really weird problem - when I query for a model instance the data comes back assigned to the wrong properties.
The model:
class SaleLineItem(models.Model):
sale = models.ForeignKey(Sale, on_delete=models.CASCADE, related_name="sale_line_items")
stock_unit = models.ForeignKey(StockUnit, on_delete=models.CASCADE, related_name="sale_line_items")
currency = models.CharField(max_length=3)
price_original = models.FloatField()
price_paid = models.FloatField()
tax_amount = models.FloatField(null=True, blank=True)
num_sold = models.IntegerField()
sale_line_item_id = models.CharField(max_length=30, null=True, blank=True)
status = models.CharField(max_length=20, choices=SALE_STATUS_CHOICES, null=True, blank=True)
The database row:
id | currency | price_original | price_paid | tax_amount | num_sold | sale_line_item_id | status | sale_id | stock_unit_id
-------+----------+----------------+------------+------------+----------+-------------------+-----------+---------+---------------
15726 | THB | 130 | 130 | | 1 | | delivered | 16219 | 2
And the query:
sli = SaleLineItem.objects.get(pk=15726)
print(sli.pk)
-------------------------
16219
print(sli.stock_unit_id)
-------------------------
THB
print(sli.currency)
-------------------------
130.0
The data get populated on the object but everything is "shifted" by one column.
But if I do the query this way:
SaleLineItem.objects.filter(pk=15726).values()
-------------------------
<QuerySet [{'id': 15726, 'sale_id': 16219, 'stock_unit_id': 2, 'currency': 'THB', 'price_original': 130.0, 'price_paid': 130.0, 'tax_amount': None, 'num_sold': 1, 'sale_line_item_id': None, 'status': 'delivered'}]>
. . . the result is correct.
I thought I might have un-migrated models but I ran both makemigrations and migrate to no effect.
Same result when I use lower-level QuerySet methods:
qs = SaleLineItem.objects.all()
clone = qs._chain()
clone.query.add_q(Q(pk=15726))
print(clone)
------------------------------
<QuerySet [<SaleLineItem: SaleLineItem object (16219)>]>
Note the pk on the model __str__ is incorrect.
Any ideas what's happening here?
Running:
Python 3.7.3
Django 2.2.1
Postgres 10
Turns out it's because I overrode __init__ with an extra (non-field) argument.
#classmethod
def from_db(cls, db, field_names, values):
if len(values) != len(cls._meta.concrete_fields):
values_iter = iter(values)
values = [
next(values_iter) if f.attname in field_names else DEFERRED
for f in cls._meta.concrete_fields
]
new = cls(*values)
new._state.adding = False
new._state.db = db
return new
Database values are populated onto the model using *values, and the model expects fields in a specific order. So you can't have an extra argument in __init__ or the order gets messed up.
Edit:
Had not read this part in the docs (https://docs.djangoproject.com/en/2.1/ref/models/instances/):
You may be tempted to customize the model by overriding the __init__
method. If you do so, however, take care not to change the calling
signature . . .

model inheritance + django admin

I am reusing the News model from cmsplugin_news, just adding some extra fields in my inheriting model. (Multi-table inheritance, just as explained here.
from cmsplugin_news.models import News
class News(News):
departments = models.ManyToManyField('department.Department', blank=True, related_name="news")
On my admin.py I am extending NewsAdmin to set my own form:
class MyNewsAdmin(NewsAdmin):
form = NewsModelForm
Which I have defined in forms.py:
from news.models import News
class NewsModelForm(NewsForm):
class Meta:
model = News
widgets = {
'excerpt': CKEditorWidget(config_name='basic'),
'content': CKEditorWidget(config_name='default')
}
def _get_widget(self):
from ckeditor.widgets import CKEditorWidget
return CKEditorWidget()
The model inheritance seems to work well when I save objects from the shell console. But when I try to create a MyNews object from the django admin and link it to a department, this field is not saved. Or at least this change is not shown anywhere
unicms-testnews=> select * from cmsplugin_news_news;
id | title | slug | excerpt | content | is_published | pub_date | created | updated | link
----+-------+------+---------+---------+--------------+------------------------+-------------------------------+-------------------------------+------
1 | dfad | dfad | | | f | 2013-09-10 13:44:46+02 | 2013-09-10 13:45:04.709556+02 | 2013-09-10 13:57:05.568696+02 |
(1 row)
unicms-testnews=> select * from news_news;
news_ptr_id
-------------
1
(1 row)
unicms-testnews=> select * from news_news_departments;
id | news_id | department_id
----+---------+---------------
1 | 1 | 1
(1 row)
I can't understand anything, can anyone help me please? Thank you very much!
You created a form for News, that also exist in your DB since the model is not abstract, not for MyNews. Thus your current form has no field for the departments attribute, even if you add a widget with an input for it. Do the code bellow instead:
class MyNewsForm(NewsForm):
class Meta:
model = MyNews # instead of just News
...
What Django does in background is to create two relations: the cmsplugin_news_news stores all the News fields, and the news_news_departments stores your new field and is in one-to-one relation with the first relation.

Django-MPTT leaf nodes of multiple types

I am developing a hirerchical application where leaf nodes can be instances of different models. I can't figure out how to make it work with django-mptt app. Is this even possible in that application? If yes, what am I doing wrong? and if no, is there anything out there what does what I am trying to do?
The following is a basic structure of the models:
class FolderItemBase(MPTTModel):
order = models.PositiveIntegerField()
class Meta:
abstract = True
class MPTTMeta:
parent_attr = 'folder'
order_insertion_by = ['order']
class Folder(FolderItemBase):
folder = TreeForeignKey('Folder', related_name='folders', blank=True, null=True)
...
class Image(FolderItemBase):
folder = TreeForeignKey('Gallery', related_name='images') # cannot be null since leaf has to be inside of a folder
...
When I try to do the following I am only able to get the Folder children, and none of the images. Same thing when I try to get ancestors of the images
>>> folder1 = Folder.objects.create(title='Folder 1', order=0)
>>> folder2 = Folder(title='Folder 2', order=0)
>>> folder2.insert_at(folder1, save=True)
>>> image = Image(...)
>>> image.insert_at(folder1, save=True)
>>> folder1.get_children()
[<Folder: Folder 2>]
>>> image.get_ancestores()
[]
And this is how things are stored in the db after all of this:
Folder table
----------------
+----+-------+-----+------+---------+-------+-----------+----------+
| ID | order | lft | rght | tree_id | level | folder_id | title |
+----+-------+-----+------+---------+-------+-----------+----------+
| 1 | 0 | 1 | 4 | 1 | 0 | | Folder 1 |
+----+-------+-----+------+---------+-------+-----------+----------+
| 2 | 0 | 2 | 3 | 1 | 1 | 1 | Folder 2 |
+----+-------+-----+------+---------+-------+-----------+----------+
Images Table
------------
+----+-------+-----+------+---------+-------+-----------+
| ID | order | lft | rght | tree_id | level | folder_id |
+----+-------+-----+------+---------+-------+-----------+
| 1 | 1 | 2 | 3 | 1 | 1 | 1 |
+----+-------+-----+------+---------+-------+-----------+
As you can see it figures what should be the level number for the image and the correct (at at least it seems to be) left and right numbers however it does not update anything in the folder table so then when you try to do a query, nothing gets selected.
Any pointers are appreciated. Thank you.
AFAIK, this is not possible; django-mptt piggy backs on Django's QuerySet, which will only ever work with one type of things. You can a use the contenttypes framework to associate the "real" item with something like FolderItem, which would only be used for the hierarchy, e.g.
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic
class FolderItem(MPTTModel):
folder = TreeForeignKey('Folder', related_name='folders', blank=True, null=True
order = models.PositiveIntegerField()
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey('content_type', 'object_id')
class Meta:
abstract = True
class MPTTMeta:
parent_attr = 'folder'
order_insertion_by = ['order']
Then, when you're using the django-mptt manager methods and such, you'll get back a queryset of FolderItems, and you can access the Folder/Image for each as you iterate over the set, through the generic foreign key.
However, be aware that this will likely be costly in terms of database queries, since each time you access a generic foreign key, a new query must be issued.