Django and computed fields in a legacy database - django

I am gradually replacing a legacy database front end with a django based system. All models are Managed = False, to preserve the original db structure.
However I have struck a problem where a field in a table is computed. The field is defined in (pseudo) sql as full_name = fname|| ||lname.
I can define the full_name field as a charfield; and I can read it no problems, however any attempt to update the record results in an update error on that field.
I can use a #property; but that replicates the functionality in django, rather than displaying the results from the db itself. Using this approach will cause problems with more complex fields (in tables I am yet to get to) that are defined with UDFs.
What is really needed is a 'read_only' or 'computed' attribute on the model itself; what is the best way to achieve this?

Do you just want to define a method in your class? Something like:
def Person(models.Model):
fname=models.CharField(...)
lname=models.CharField(...)
def fullname(self):
return self.fname+" "+self.lname
(not exactly sure what Managed=False means...)

if you are trying to make calculation on a database models and pass the value of a model field to another model field of the same class model, using a defined function then this solution might help you. for example lets assume you have an investment company and you give 20% per month for the capital each user invested, you would want want to pass value from capital model to a function that calculates the percentage interest, and then you will pass that function into another field monthly_payment and get saved in the database.
1) pip install django-computed-property
2) add 'computed_property' to your installed apps in project settings.
3) in your models.py, import computed_property then
class Investment(models.Model):
name = models.CharField(max_length=200)
capital = models.FloatField(null=False)
percentage = models.CharField(max_length=5)
duration = models.CharField(max_length=10, default="1 months")
monthly_payment = computed_property.ComputedFloatField( compute_from='monthly_percentage', null=False, blank=False)
then your function to perform the calculation will go thus
#property
def monthly_percentage(self):
return (20 / 100) * self.capital
Note: what i discovered was if you use the inbuilt django fields be it FloatFiled or IntegerField, this function won't read the amount you are passing in to get your 20% calculations.i hope this works for you as i stated all as they worked for me,cheers.

Related

How can I access a value from one model and pass to another model in Django?

I have one model called Weight (filled by User input/choice) and another called Enterprise.
class Weight(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="weights")
weight_of_history = models.IntegerField(null=True, blank=True)
class Enterprise(models.Model):
...
The weight is saved, one per user, and replaced everytime the user choose new one.
Inside the Enterprise class, I am creating an property that must get the "weight_of_history" (depending on the user, who has chosen the weight) from Weight class, but the models have no Foreign key or related name between them.
class Enterprise(models.Model):
...
#property
def ranking(self):
weight_of_history = <-- HERE I NEED TO TAKE WEIGHT_HISTORY FROM THE FIRST MODEL
THEN I COULD CALCULATE
How could I do that? Thank you!
You can use django's powerful query functionality and fetch the required objects from the db. Here are the docs that might help you with that. Django docs are amazing, so I would recommend that you read up on queries, models, and forms to have easier time with you project.
For your example we can fetch all the weights for the user in one query by filtering the weights by user. In fact, django ORM allows for chaining filters and you can create really sophisticated queries
class Enterprise(models.Model):
...
#property
def ranking(self):
weight_of_history = Weight.objects.filter(user=some_user)
If you do not know the user beforehand, then you can do inside the view and grab the user that makes the request and filter using this user:
#views.py
user_making_request = request.user
weight_of_history = Weight.objects.filter(user=user_making_request)

Django making field related to other field (object) value

few years ego I worked with Odoo framework. and Odoo has very nice feature like this:
partner_id = field.Many2one(Partner)
partner_name = fields.Char(string='Partner name', related='partner_id.name')
basically whenever you would assign different partner_id from Partner table, partner_name would be assigned automatically. Now I started to work with django (absolute newbie), and I can't seem to find a similar functionality.
My question is what could be possible solution for this problem. Maybe there are already established external libraries that has this sort of functionality?
Expected result:
product = models.ForeignKey(Product)
product_color = models.CharField(string='Partner name', related='product.color')
having in mind that product object would have color field and it would be assigned to product_color whenever product field value Product object color value changes. Also what about storing it to database? Would be nice if there was an option to chose between storing it in database or getting it on the fly.
Cheers!
Creating a getter is pretty easy, because you can simply have functions in a Python object behave as a property:
class SampleModel(models.Model):
product = models.ForeignKey(Product)
#property
def product_color(self):
return self.product.color
This does retrieve the property on the fly, which will cause a call to the database.
Duplicating data, is usually a (more severe) antipattern. Synchronizing data, even in two tables in the same database, often turns out harder than one might expect. Even if you would use Django's signal framework for example, then some Django ORM calls can circumvent that (for example .update(..) [Django-doc]). But even if you somehow would cover those cases, then another program that talks to the database could update one of the two fields.
Most databases have triggers that can help. But again, the number of cases to cover are often larger than expected. For example, if the Product that we refer to is removed, then or the foreign key now points to a different Product, then we will need to update that field.
Therefore it is often better, to fetch the name of the related product when we need it. We can do so by (a) defining a property; or (b) make an annotation, for example in the manager.
Defining a property
We can define a property that will load the related product, and fetch the related name, like:
class Order(models.Model):
product = models.ForeignKey(Product, on_delete=models.PROTECT)
#property
def product_name(self):
return self.product.name
Then we can fetch the product name with some_order.product_name. This might not be very efficient if we need to fetch it often, since the relations are, by default, loaded lazily in Django, and thus can result in an N+1 problem.
Annotate the queryset
We can make an annotation that will fetch the name of the product in the same query when we fetch the Order, for example:
from django.db.models import F
class OrderManager(models.Manager):
def get_queryset(self):
return super().get_queryset().annotate(
product_name=F('product__name')
)
class Order(models.Model):
product = models.ForeignKey(Product, on_delete=models.PROTECT)
objects = OrderManager()
Then if we fetch an order. For example with Order.objects.get(pk=1), then that Order object will have an attribute product_name with the name of the product.

Object not found, but does exist

I have an existing working site, that takes advantage of Adobe Lightroom's catalog being an sqlite3 db. The model is auto-generated.
Whilst updating the code for the site (to TemplateView and streamlining), I thought I'd get adventurous and pull on one of the class models to get the information about how many times a face (person) appears in the photos.
The model:
class Aglibrarykeywordpopularity(models.Model):
id_local = models.IntegerField(primary_key=True, blank=True, null=False)
occurrences = models.TextField() # This field type is a guess.
popularity = models.TextField() # This field type is a guess.
tag = models.TextField(unique=True) # This field type is a guess.
class Meta:
managed = False
db_table = 'AgLibraryKeywordPopularity'
The 'tag' value is obtainable from another model, where it's called 'id_local':
face_id = list(Aglibrarykeyword.objects.values_list('id_local', flat=True))
The values for the 'face_id' are all present in the previous 'tag' field when you view the db.
However, when using:
for a in face_id:
face_pop.append(Aglibrarykeywordpopularity.objects.get(tag=a).occurrences)
.. it complains that there are no matches. I've substituted a for a number, or string of the number, even adding .0 to the representation, and it still complains no matches. I've tried .filter...first() etc.
slides.models.DoesNotExist: Aglibrarykeywordpopularity matching query does not exist
My current work-around is going to output All() of the objects to lists and obtain the values I want that way - which works, but I'm interested more in why it's not working as it should.
Something I notice is that the model has tag defined as TextField, and when I obtain the values myself from All() they are float numbers. Is it the case that there's a sort of 3 way mismatching of variable types, 1)the model 2)the data 3)the query - so no matter if I put a float number or text in my query, it will never match? i.e. do I have to change the model, migrate it and all should be well? (as I'm no Django expert, does the "managed = False" mean that even if I do this, it will not effect the db data?)
TIA for comments.

Django models relationship collision

I'm django learner and i have problem with django relationship concept.
I have written two models that have relations with single source model (UserProfile).but one of them does not work properly.The Message class work fine with it's two fields (sender,receiver),but the other class(Class) lead to
programing error:relation "CEA_class" already exists
where CEA is my app name.I really prefer to have two different field for the class and don't join them as single field.What I'm suppose to do with it?
class Message ---->that work fine
class Message(models.Model):
sender = models.ForeignKey(UserProfile,blank=False,related_name="sender")
receiver = models.ForeignKey(UserProfile,blank=False,related_name="receiver")
content = models.CharField(max_length=200)
priority = models.BigIntegerField(choices=PRIORITY_LIST,default=1)
class Class ---->that lead to error
class Class(models.Model):
subject = models.CharField(max_length=20)
time = models.CharField(max_length=20)
day = models.CharField(max_length=20)
location = models.CharField(max_length=20)
students = models.ManyToManyField(UserProfile,blank=True,related_name="students")
master = models.ForeignKey(UserProfile,blank=True,related_name="master")
Here is my whole UserProfile class
class UserProfile(models.Model):
user=models.OneToOneField(User,
related_name='UserProfile',on_delete=models.CASCADE)
field=models.CharField(max_length=20,default="ce")
userPhoto=models.ImageField(upload_to='documents/',blank=True)
Type=models.BigIntegerField(choices=USER_TYPE,default=2)
gender=models.BigIntegerField(choices=GENDER_TYPE,default=1)
def __str__(self):
return self.user.username
#The decorator can also takes extra arguments which are passed onto the
signal
#receiver(post_save,sender=User)
def create_or_update_UserProfile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
instance.UserProfile.save()
I have an error in my admin page as below:
.
ProgrammingError at /admin/CEA/class/
column CEA_class.master_id does not exist LINE 1: ...time",
"CEA_class"."day", "CEA_class"."location", "CEA_class...
I solved this a while ago and found some references including in the Django doc, but I'm actually still not quite sure why this is needed as the underlying SQL structure doesn't (as far as I can tell in looking at my own database) show the variance in related_name - it appears to be an internal Django issue rather than an actual SQL issue. In any case, when you have more than one field related to the same other model within a table then all except one have to have something referencing the class in order to get Django to keep things straight. It is often cleanest to add class to ALL the related_names within a model that has the problem for consistency, though I have checked my own code and sometimes I do that, sometimes I don't:
students = models.ManyToManyField(UserProfile,blank=True,related_name="%(class)s_students")
master = models.ForeignKey(UserProfile,blank=True,related_name="%(class)s_master")
After all i came to this conclusion that the best way for deleting the Annoying irrelevant data that cause the problems is to temporary rename the model and migrate and then rename it to it's first name.By this way django itself take care of deleting relations and every data in database that causes your problem.
With all these interpretations,all of the suggested answers was helpful but i think the simple is better,why not. :))

Dynamic model choice field in django formset using multiple select elements

I posted this question on the django-users list, but haven't had a reply there yet.
I have models that look something like this:
class ProductGroup(models.Model):
name = models.CharField(max_length=10, primary_key=True)
def __unicode__(self): return self.name
class ProductRun(models.Model):
date = models.DateField(primary_key=True)
def __unicode__(self): return self.date.isoformat()
class CatalogItem(models.Model):
cid = models.CharField(max_length=25, primary_key=True)
group = models.ForeignKey(ProductGroup)
run = models.ForeignKey(ProductRun)
pnumber = models.IntegerField()
def __unicode__(self): return self.cid
class Meta:
unique_together = ('group', 'run', 'pnumber')
class Transaction(models.Model):
timestamp = models.DateTimeField()
user = models.ForeignKey(User)
item = models.ForeignKey(CatalogItem)
quantity = models.IntegerField()
price = models.FloatField()
Let's say there are about 10 ProductGroups and 10-20 relevant
ProductRuns at any given time. Each group has 20-200 distinct
product numbers (pnumber), so there are at least a few thousand
CatalogItems.
I am working on formsets for the Transaction model. Instead of a
single select menu with the several thousand CatalogItems for the
ForeignKey field, I want to substitute three drop-down menus, for
group, run, and pnumber, which uniquely identify the CatalogItem.
I'd also like to limit the choices in the second two drop-downs to
those runs and pnumbers which are available for the currently
selected product group (I can update them via AJAX if the user
changes the product group, but it's important that the initial page
load as described without relying on AJAX).
What's the best way to do this?
As a point of departure, here's what I've tried/considered so far:
My first approach was to exclude the item foreign key field from the
form, add the substitute dropdowns by overriding the add_fields
method of the formset, and then extract the data and populate the
fields manually on the model instances before saving them. It's
straightforward and pretty simple, but it's not very reusable and I
don't think it is the right way to do this.
My second approach was to create a new field which inherits both
MultiValueField and ModelChoiceField, and a corresponding
MultiWidget subclass. This seems like the right approach. As
Malcolm Tredinnick put it in
a django-users discussion,
"the 'smarts' of a field lie in the Field class."
The problem I'm having is when/where to fetch the lists of choices
from the db. The code I have now does it in the Field's __init__,
but that means I have to know which ProductGroup I'm dealing with
before I can even define the Form class, since I have to instantiate the
Field when I define the form. So I have a factory
function which I call at the last minute from my view--after I know
what CatalogItems I have and which product group they're in--to
create form/formset classes and instantiate them. It works, but I
wonder if there's a better way. After all, the field should be
able to determine the correct choices much later on, once it knows
its current value.
Another problem is that my implementation limits the entire formset
to transactions relating to (CatalogItems from) a single
ProductGroup.
A third possibility I'm entertaining is to put it all in the Widget
class. Once I have the related model instance, or the cid, or
whatever the widget is given, I can get the ProductGroup and
construct the drop-downs. This would solve the issues with my
second approach, but doesn't seem like the right approach.
One way of setting field choices of a form in a formset is in the form's __init__ method by overwriting the self.fields['field_name'].choices, but since a more dynamic approach is desired, here is what works in a view:
from django.forms.models import modelformset_factory
user_choices = [(1, 'something'), (2, 'something_else')] # some basic choices
PurchaserChoiceFormSet = modelformset_factory(PurchaserChoice, form=PurchaserChoiceForm, extra=5, max_num=5)
my_formset = PurchaserChoiceFormSet(self.request.POST or None, queryset=worksheet_choices)
# and now for the magical for loop
for choice_form in my_formset:
choice_form.fields['model'].choices = user_choices
I wasn't able to find the answer for this but tried it out and it works in Django 1.6.5. I figured it out since formsets and for loops seem to go so well together :)
I ended up sticking with the second approach, but I'm convinced now that it was the Short Way That Was Very Long. I had to dig around a bit in the ModelForm and FormField innards, and IMO the complexity outweighs the minimal benefits.
What I wrote in the question about the first approach, "It's straightforward and pretty simple," should have been the tip-off.