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
Related
I have the following two models (just for a test):
class IdGeneratorModel(models.Model):
table = models.CharField(primary_key=True, unique=True,
null=False, max_length=32)
last_created_id = models.BigIntegerField(default=0, null=False,
unique=False)
#staticmethod
def get_id_for_table(table: str) -> int:
try:
last_id_set = IdGeneratorModel.objects.get(table=table)
new_id = last_id_set.last_created_id + 1
last_id_set.last_created_id = new_id
last_id_set.save()
return new_id
except IdGeneratorModel.DoesNotExist:
np = IdGeneratorModel()
np.table = table
np.save()
return IdGeneratorModel.get_id_for_table(table)
class TestDataModel(models.Model):
class Generator:
#staticmethod
def get_id():
return IdGeneratorModel.get_id_for_table('TestDataModel')
id = models.BigIntegerField(null=False, primary_key=True,
editable=False, auto_created=True,
default=Generator.get_id)
data = models.CharField(max_length=16)
Now I use the normal Django Admin site to create a new Test Data Set element. What I expected (and maybe I'm wrong here) is, that the method Generator.get_id() is called exactly one time when saving the new dataset to the database. But what really happens is, that the Generator.get_id() method is called three times:
First time when I click the "add a Test Data Set" button in the admin area
A second time shortly after that (no extra interaction from the user's side)
And a third time when finally saving the new data set
The first time could be OK: This would be the value pre-filled in a form field. Since the primary key field is not displayed in my form, this may be an unnecessary call.
The third time is also clear: It's done before saving. When it's really needed.
The code above is only an example and it is a test for me. In the real project I have to ask a remote system for an ID instead from another table model. But whenever I query that system, the delivered ID gets locked there - like the get_id_for_table() method counts up.
I'm sure there are better ways to get an ID from a method only when really needed - the method should be called exactly one time - when inserting the new dataset. Any idea how to achieve that?
Forgot the version: It's Django 1.8.5 on Python 3.4.
This is not an answer to your question, but could be a solution to your problem
I believe this issue is very complicated. Especially because you want a transaction that spans a webservice call and a database insert... What I would use in this case: generate a uuid locally. This value is practially guaranteed to be unique in the 4d world (time + location) and use that as id. Later, when the save is done, sync with your remote services.
Following this answer, I tried to split my SQL Story table into parent/children - with the children holding the specific user data, the parent more generic data. Now I've run into a problem that betrays my lack of experience in Django. My user page attempts to show a list of all the stories that a user has written. Before, when my user page was only pulling data from the story table, it worked fine. Now I need to pull data from two tables with linked info and I just can't work out how to do it.
Here's my user_page view before attempts to pull data from the parent story table too:
def user_page(request, username):
user = get_object_or_404(User, username=username)
userstories = user.userstory_set.order_by('-id')
variables = RequestContext(request, {
'username': username,
'userstories': userstories,
'show_tags': True
})
return render_to_response('user_page.html', variables)
Here is my models.py:
class story(models.Model):
title = models.CharField(max_length=400)
thetext = models.TextField()
class userstory(models.Model):
main = models.ForeignKey(story)
date = models.DateTimeField()
user = models.ForeignKey(User)
I don't really know where to start in terms of looking up the appropriate information in the parent table too and assinging it to a variable. What I need to do is follow the 'main' Key of the userstory table into the story table and assign the story table as a variable. But I just can't see how to implement that in the definition.
EDIT: I've tried story = userstory.objects.get(user=user) but I get 'userstory matching query does not exist.'
Reading through your previous question that you linked to, I've discovered where the confusion lies. I was under the impression that a Story may have many UserStorys associated with it. Note that I'm using Capital for the class name, which is common Python practise. I've made this assumption because your model structure is allowing this to happen with the use of a Foreign Key in your UserStory model. Your model structure should look like this instead:
class Story(models.Model):
title = models.CharField(max_length=400)
thetext = models.TextField()
class UserStory(models.Model):
story = models.OneToOneField(Story) # renamed field to story as convention suggests
date = models.DateTimeField()
user = models.ForeignKey(User)
class ClassicStory(models.Model)
story = models.OneToOneField(Story)
date = models.DateTimeField()
author = models.CharField(max_length=200)
See the use of OneToOne relationships here. A OneToOne field denotes a 1-to-1 relationship, meaning that a Story has one, and only one, UserStory. This also means that a UserStory is related to exactly one Story. This is the "parent-child" relationship, with the extra constraint that a parent has only a single child. Your use of a ForeignKey before means that a Story has multiple UserStories associated with it, which is wrong for your use case.
Now your queries (and attribute accessors) will behave like you expected.
# get all of the users UserStories:
user = request.user
stories = UserStory.objects.filter(user=user).select_related('story')
# print all of the stories:
for s in stories:
print s.story.title
print s.story.thetext
Note that select_related will create a SQL join, so you're not executing another query each time you print out the story text. Read up on this, it is very very very important!
Your previous question mentions that you have another table, ClassicStories. It should also have a OneToOneField, just like the UserStories. Using OneToOne fields in this way makes it very difficult to iterate over the Story model, as it may be a "ClassicStory" but it might be a "UserStory" instead:
# iterate over ALL stories
allstories = Story.objects.all()
for s in allstories:
print s.title
print s.thetext
print s.userstory # this might error!
print s.classicstory # this might error!
See the issue? You don't know what kind of story it is. You need to check the type of story it is before accessing the fields in the sub-table. There are projects that help manage this kind of inheritance around, an example is django-model-utils InheritanceManager, but that's a little advanved. If you never need to iterate over the Story model and access it's sub tables, you don't need to worry though. As long as you only access Story from ClassicStories or UserStories, you will be fine.
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]
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.))
I have some Django models that record people's listening habits (a bit like Last.fm), like so:
class Artist(models.Model):
name = models.CharField()
class Song(models.Model):
artist = models.ForeignKey(Artist)
title = models.CharField()
class SongPlay(models.Model):
song = models.ForeignKey(Song)
user = models.ForeignKey(User)
time = models.DateTimeField()
class User(models.Model):
# doesn't really matter!
I'd like to have a user page where I can show the top songs that they've listened to in the past month. What's the best way to do this?
The best I've come up with so far is:
SongPlay.past_month
.filter(user=user)
.values('song__title', 'song__id', 'song__artist__name')
.annotate(plays=Count('song'))
.order_by('-plays')[:20]
Above, past_month is a manager that just filters plays from the last month. Assume that we've already got the correct user object to filter by as well.
I guess my two questions are:
How can I get access to the original object as well as the plays annotation?
This just gives me certain values, based on what I pass to values. I'd much rather have access to the original object – the model has methods I'd like to call.
How can I group from SongPlay to Artist?
I'd like to show a chart of artists, as well as a chart of songs.
You can use the same field in both values and annotate.
You have the primary key of the Song object (you could just use song instead of song__id), so use
Song.objects.get(id=...)
For your second question, do a separate query with song__artist as the field in values and annotate:
from django.db.models import Count
SongPlay.past_month
.filter(user=user)
.values('song__artist')
.annotate(plays=Count('song__artist'))
.order_by('-plays')[:20]
agf has already showed you how to group by song_artist. What I would do to get the actual Song object is store it in memcached, or if the method you are calling is rather simplistic make it a static method or a class method. You might could also initialize a Song object with the data from the query and not actually save it to get access to this method. Might help to know the details of the methods you want to call from the Song object.