How to check for a specific field change in a model? - python

I have my models.py :
class Hotel(models.Model):
name = models.CharField(max_length=20)
currency = models.ForeignKey(Currency)
class Currency(models.Mode):
code = models.CharField(max_length=3)
name = models.CharField(max_length=10)
Whenever the currency field in hotel is changing I need to be able to do something. So I have a function like :
#receiver(pre_save,sender=Hotel)
def update_something(sender,**kwargs)
obj = kwargs['instance']
old_object = Hotel.objects.get(pk=obj.pk)
'''
Now I can do anything here comparing the oldo object with the instance
'''
The thing is I don't want to make a query for this, since then the purpose of signals becomes stupid and I become a fool.
So I should be able to do something like :
updated = kwargs['update_fields']
new_currency = updated['currency']
Is their a way I can find out that change for ONLY one particular field say currency ,instead of doing a query like this. I want to get the changes related to the currency foreign key and update things before I save.
Sorry for my bad English and not being able to use too technical terms.
Thanks :)

Rather hacky solution is to save object state on initialization.
from django.forms.models import model_to_dict
class Currency(models.Mode):
code = models.CharField(max_length=3)
name = models.CharField(max_length=10)
def __init__(self):
super(Currency, self).__init__()
self.__state = model_to_dict(self)
def updated(self):
new_state = model_to_dict(self)
return dict(set(self.__state.iteritems()) - set(new_state.iteritems()))
Method updated will return difference between initial and new states.

The point of signals is better decoupling - allow other applications (that you don't necessarily know yet) to transparently hook into your application and/or avoid circular dependencies between application. There's really no point in using a signal here afaik (unless of course the signal handler is in another app and you don't want the app with the Hotel and Currency models to depend on this other app).
To answer your question: doing a query to retrieve the original state is the only way you can compare stored value and current value. Now if you're only interested in one specific field, you of course don't have to retrieve the whole model instance - just use queryset.values_list:
old_currency = Hotel.objects.filter(pk=obj.pk).values_list("currency", flat=True)[0]

Related

How to write manager class which use filter field as computed field not as a part of model fields?

I have a model Student with manager StudentManager as given below. As property gives the last date by adding college_duration in join_date. But when I execute this property computation is working well, but for StudentManager it gives an error. How to write manager class which on the fly computes some field using model fields and which is used to filter records.
The computed field is not in model fields. still, I want that as filter criteria.
class StudentManager(models.Manager):
def passed_students(self):
return self.filter(college_end_date__lt=timezone.now())
class Student(models.Model):
join_date = models.DateTimeField(auto_now_add=True)
college_duration = models.IntegerField(default=4)
objects = StudentManager()
#property
def college_end_date(self):
last_date = self.join_date + timezone.timedelta(days=self.college_duration)
return last_date
Error Django gives. when I tried to access Student.objects.passed_students()
django.core.exceptions.FieldError: Cannot resolve keyword 'college_end_date' into field. Choices are: join_date, college_duration
Q 1. How alias queries done in Django ORM?
By using the annotate(...)--(Django Doc) or alias(...) (New in Django 3.2) if you're using the value only as a filter.
Q 2. Why property not accessed in Django managers?
Because the model managers (more accurately, the QuerySet s) are wrapping things that are being done in the database. You can call the model managers as a high-level database wrapper too.
But, the property college_end_date is only defined in your model class and the database is not aware of it, and hence the error.
Q 3. How to write manager to filter records based on the field which is not in models, but can be calculated using fields present in the model?
Using annotate(...) method is the proper Django way of doing so. As a side note, a complex property logic may not be re-create with the annotate(...) method.
In your case, I would change college_duration field from IntegerField(...) to DurationField(...)--(Django Doc) since its make more sense (to me)
Later, update your manager and the properties as,
from django.db import models
from django.utils import timezone
class StudentManager(models.Manager):
<b>def passed_students(self):
default_qs = self.get_queryset()
college_end = models.ExpressionWrapper(
models.F('join_date') + models.F('college_duration'),
output_field=models.DateField()
)
return default_qs \
.annotate(college_end=college_end) \
.filter(college_end__lt=timezone.now().date())</b>
class Student(models.Model):
join_date = models.DateTimeField()
college_duration = models.DurationField()
objects = StudentManager()
#property
def college_end_date(self):
# return date by summing the datetime and timedelta objects
return <b>(self.join_date + self.college_duration).date()
Note:
DurationField(...) will work as expected in PostgreSQL and this implementation will work as-is in PSQL. You may have problems if you are using any other databases, if so, you may need to have a "database function" which operates over the datetime and duration datasets corresponding to your specific database.
Personally, I like this solution,
To quote #Willem Van Olsem's comment:
You don't. The database does not know anything about properties, etc. So it can not filter on this. You can make use of .annotate(..) to move the logic to the database side.
You can either do the message he shared, or make that a model field that auto calculates.
class StudentManager(models.Manager):
def passed_students(self):
return self.filter(college_end_date__lt=timezone.now())
class Student(models.Model):
join_date = models.DateTimeField(auto_now_add=True)
college_duration = models.IntegerField(default=4)
college_end_date = models.DateTimeField()
objects = StudentManager()
def save(self, *args, **kwargs):
# Add logic here
if not self.college_end_date:
self.college_end_date = self.join_date + timezone.timedelta(days-self.college_duration)
return super.save(*args, **kwargs)
Now you can search it in the database.
NOTE: This sort of thing is best to do from the start on data you KNOW you're going to want to filter. If you have pre-existing data, you'll need to re-save all existing instances.
Problem
You’re attempting to query on a row that doesn’t exist in the database. Also, Django ORM doesn’t recognize a property as a field to register.
Solution
The direct answer to your question would be to create annotations, which could be subsequently queried off of. However, I would reconsider your table design for Student as it introduces unnecessary complexity and maintenance overhead.
There’s much more framework/db support for start date, end date idiosyncrasy than there is start date, timedelta.
Instead of storing duration, store end_date and calculate duration in a model method. This makes more not only makes more sense as students are generally provided a start date and estimated graduation date rather than duration, but also because it’ll make queries like these much easier.
Example
Querying which students are graduating in 2020.
Students.objects.filter(end_date__year=2020)

ForeignKey vs CharField

I have an idea for data model in django and I was wondering if someone can point out pros and cons for these two setups.
Setup 1: This would be an obvious one. Using CharFields for each field of each object
class Person(models.Model):
name = models.CharField(max_length=255)
surname = models.CharField(max_length=255)
city = models.CharField(max_length=255)
Setup 2: This is the one I am thinking about. Using a ForeignKey to Objects that contain the values that current Object should have.
class Person(models.Model):
name = models.ForeignKey('Name')
surname = models.ForeignKey('Surname')
city = models.ForeignKey('City')
class Chars(models.Model):
value = models.CharField(max_length=255)
def __str__(self):
return self.value
class Meta:
abstract = True
class Name(Chars):pass
class Surname(Chars):pass
class City(Chars):pass
So in setup 1, I would create an Object with:
Person.objects.create(name='Name', surname='Surname', city='City')
and each object would have it's own data. In setup 2, I would have to do this:
_name = Name.objects.get_or_create(value='Name')[0]
_surname = Surname.objects.get_or_create(value='Surname')[0]
_city = City.objects.get_or_create(value='City')[0]
Person.objects.create(name=_name, surname=_surname, city=_city)
Question: Main purpose for this would be to reuse existing values for multiple objects, but is this something worth doing, when you take into consideration that you need multiple hits on the database to create an Object?
Choosing the correct design pattern for your application is a very wide area which is influenced by many factors that are even possibly out of scope in a Stack Overflow question. So in a sense your question could be a bit subjective and too broad.
Nevertheless, I would say that assigning a separate model (class) for first name, another separate for last name etc. is an overkill. You might essentially end up overengineering your app.
The main reasoning behind the above recommendation is that you probably do not want to treat a name as a separate entity and possibly attach additional properties to it. Unless you really would need such a feature, a name is usually a plain string that some users happen to have identical.
It doesn't make any good to keep name and surname as separate object/model/db table. In your setup, if you don't set name and surname as unique, then it doesn't make any sense to put them in separate model. Even worse, it will incur additional DB work and decrease performance. Now, if you set them as unique, then you have to work over the situation when, e.g. some user changes his name and by default it would be changed for all users with that name.
On the other hand, city - there're not that many cities and it's a good idea to keep it as separate object and refer to it via foreign key from user. This will save disk space, allow to easily get all users from same city. Even better, you can prepopulate cities DB and provide autocompletion fro users entering there city. Though for performance you might still want to keep city as a string on the user model.
Also, to mention 'gender' field, since there're not many possible choices for this data, it's worth to use enumeration in your code and store a value in DB, i.e. use choices instead of ForeignKey to a separate DB table.

How to add custom field to mongoengine model?

I have a Project model as follows:
class Project(me.Document):
title = me.StringField(max_length=64, required=True, unique=True)
start_date = me.DateTimeField(default=datetime.utcnow())
end_date = me.DateTimeField(default=datetime.utcnow())
duration = me.IntField() # sprint duration
sequence = me.IntField()
def __init__(self, *args, **values):
super(Project, self).__init__( *args, **values)
def __str__(self):
return self.title
def get_current_sprint(self):
''' A logic here to calculate the current sprint.'''
And anther model sprint:
class Sprint(me.Document):
start_date = me.DateTimeField()
end_date = me.DateTimeField()
sequence = me.IntField(required=True, default=0, unique_with='project')
project = me.ReferenceField('Project')
If I have project instance then I can get current sprint by calling the method as
project.get_current_sprint()
But What I am trying to is ; whenever a project object is being queried, rather than calling a method to get the current sprint, it should have an attribute project.current_sprint which has the current sprint info.
Is there a way to achieve it?
Any help would be really appreciated.
I think the concept of what you're looking for is called Database References in MongoDB.
In MongoEngine, you would probably create a ReferenceField in your Project model, which would reference a Sprint document.
I am trying to achieve something similar, and while I don't know the entire answer, I'll post what I have identified. The thing you probably want done should be presumably enabled by a query set (which you would access through Project.objects). Mongoengine creates one, but allows you to replace it, so that when you get (Project.objects.get(...)) for instance, it might "prefetches" the sprint relevant to this project. How to do that is probably through the mongoengine query syntax, which I'm not yet familiar with.
In the end, it's possible you'll have to combine properties and cache to achieve what you want. the queried project will have a dynamic reference to a sprint (say project.sprint) and you could have a property on Project to check whether this data exists (and if not, query it).

Django Mongodb ListField not saving or updating

I am starting to create a webapp using Django and MongoDB. Everything is working fine when I create a model and save it into the Database. Now, I do a "Class.objects.get()" to get the object I need from my DB and I have one field called "media" which is a ListField(). I had tried doing either:
Concert.media.append(list)
or
Concert.media.extend(list)
and then
Concert.save()
This is my "Concert" object in my models.py:
class Concert(models.Model):
main_artist = models.CharField(max_length=50)
concert_id = models.AutoField(primary_key=True)
openers = ListField(EmbeddedModelField('Opener'))
concert_date = models.DateField()
slug = models.SlugField(unique=True)
media = ListField()
And when I go to see the results in does not update the object. No values where saved. If someone can help me I going to give a super cyber fist bump.
Concert is a class, not an instance. You can't save a class. You need to make an instance of the class and save that. Something like
c = Concert()
c.media.append(list)
c.save()
(btw, just as a note, list is a bad variable name because list is a type in python. Never use types as variable names (though everyone is guilty of this at one point or another, including me.))

Django dynamic class cannot be updated

I have a set of DB tables which store customer order counts per minute per day. Each month of the year is a different table in order to avoid excessively large tables. In order to retrieve this data in my Django webpage, I dynamically create a model class with db_table being populated based on the date received from an html form input. The problem is that when I resubmit the form with a new date value, the class does not update to what should be the new model, it still maintains the old value.
My models.py looks something like this:
class baseModel(models.Model):
id = models.CharField(max_length=40)
date = models.IntegerField()
minute = models.IntegerField()
totalorders = models.IntegerField()
class Meta:
abstract = True
managed = False
def getModel(type, yyyymm):
if type == 'duration':
class DurationClass(baseModel):
medianduration = models.IntegerField()
avgduration = models.IntegerField()
class Meta:
db_table='orderTable' + yyyymm
#debug statement
print db_table
return DurationClass
yyyymm is just a string, like '201204' for April 2012. So if I enter April 2012 into the input box it works fine but then if I change to March 2012, it still queries for April data. I can see from the debug statement that db_table is being properly updated, but for some reason it's not working. Do I need to deallocate the old dynamic model before allocating a new one or something? In view.py, I'm just doing this (not in a loop):
myModel = getModel('duration', startyyyymm)
QS = myModel.objects.using( ...etc
Many thanks for any ideas.
You have a problem about how python manage the creation of dynamic clases. I don't know exactly how python works, but it seems to be that the way you do it is not totally correct. I think it is because python classes are attached to one module, so the first time you execute "getModel" it creates the model as you expect. But, after that, every time you execute "getModel", as the class has always the same name, python can't create the same class at the same module, so it somehow returns you the same class you create the first time you call "getModel". (I hope you understand my English, although i might be wrong about how python dynamic classes creation works)
I search a little and make some tests before giving you an answer. It seems to be that the best way of creating a dynamic class is using "type" (python built-in method), so you can create one class per table (this classes must have a different name).
Here's an example of what you can do (it worked for me):
def getModel(type, yyyymm):
if type == 'duration':
newModelClass = type(
'newModelName', #It could be the table name you are going to search in. It must be different for every different db table you want to use. For example: 'orderTable' + yyyymm
(baseModel, ), #Base class for your new model
{
'medianduration' : models.IntegerField(), #New model's attribute
'avgduration' : models.IntegerField(), #New model's attribute
'__module__':__name__, #This is required. If not given, type raises a KeyError
'Meta': type(
'Meta',
(object,),
{
'db_table':'orderTable' + yyyymm, #Here you put the table name you want to use
'__module__':__name__,
}
)
}
)
return newModelClass
If i didn't make any copy/paste mistake, it should now work for you.
Again, sorry if i make any English mistake. I'm a little bit rusty at writing in English and in English in general :S
I hope it helps you. Althought i agree that your database should work fine without using multiple tables...
All you need is given the below link: https://code.djangoproject.com/wiki/DynamicModels

Categories