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.
Related
I have created several models using Django, for one of the models I would like to have a trigger that does not allow deletes. See the model code below.
#pgtrigger.register(
pgtrigger.Protect(name='protect_deletes',operation=pgtrigger.Delete)
)
class NoDelete(models.Model):
salary = models.PositiveIntegerField()
id = models.PositiveIntegerField(primary_key=True)
name = models.CharField(max_length=50)
The application also has an website that allows you to interact with the database, when deleting a model of this type a function in views.py is called.
def delete(request, id):
temp = NoDelete.objects.get(pk=id)
temp.delete()
So it seems like the trigger does not function as I want it to since this delete removes the row from the database. When trying to delete the tuple directly from the database however the trigger is fired. Any ideas on how to get around this issue? I have to use a trigger in this assignment.
Is there a better way to design the Message model ?
I have a Message model:
class Message(models.Model):
"""
message
"""
title = models.CharField(max_length=64, help_text="title")
content = models.CharField(max_length=1024, help_text="content")
is_read = models.BooleanField(default=False, help_text="whether message is read")
create_user = models.ForeignKey(User, related_name="messages",help_text="creator")
receive_user = models.CharField(max_length=1024, help_text="receive users' id")
def __str__(self):
return self.title
def __unicode__(self):
return self.title
You see, I use models.CharField to store the users' id, so I can know the users who should receive this row message.
I don't know whether this design type is good. or is there a better way to do that?
I have considered use ManyToMany Field, but I think if user is too many, the admin create one message will create as many as users count, so I think this is not a good idea.
I would definitely use ManyToManyField for your receive_user. You're going to find that keeping a CharField updated and sanitised with user_ids is going to be a nightmare that will involve re-implementing vast swathes of existing Django functionality.
I'm not sure if I understand your potential issue to using ManyToManyField, users of the admin will be able to select which users are to be recipients of the message, it doesn't automatically a message for each user.
e: Also, depending on which version of python you're using (2 or 3) you only need one of either __str__ or __unicode__
__unicode__ is the method to use for python2, __str__ for python3: See this answer for more details
So it actually depends on your needs in which direction I would change your message Model.
General Changes
Based on the guess: you don't ever need an index on the content field
I would change the content to a TextField (alse because the length of 1024 is already to large for a propper index on mysql for example) https://docs.djangoproject.com/en/1.11/ref/databases/#textfield-limitations here some more infos about this topic.
I would pbly increase the size of the title field just because it seems convenient to me.
1. Simple -> One User to One User
The single read field indicates a one to one message:
I would change the Receiver to also be a Foreign key and adapt the related names of the sender and receiver field to represent these connections to something like sent-messages and received-messages.
Like #sebastian-fleck already suggested I'd also change the read field to a datetime field, it only changes your querysets from filter(read=True) to filter(read__isnull=False) to get the same results and you could create a property representing the read as boolean for conveniance, e.g.
#property
def read(self):
return bool(self.read_datetime) # assumed read as a datetime is read_datetime
2. More Complex: One User to Multiple User
This can get a lot more complex, here the least complex solution I could think of.
Conditions:
- there are only messages and no conversation like strukture
- a message should have a read status for every receiver
(I removed descriptions for an easier overview and changed the models according to my opinions from before, this is based on my experience and the business needs I assumed from your example and answers)
#python_2_unicode_compatible
class Message(models.Model):
title = models.CharField(max_length=160)
content = models.TextField()
create_user = models.ForeignKey(User, related_name="sent-messages")
receive_users = models.ManyToManyField(User, through=MessageReceiver)
def __str__(self):
return 'Message: %s' % self.title
#python_2_unicode_compatible
class MessageReceiver(models.Model):
is_read = models.Datetime(null=True, blank=True)
receiver = models.ForeignKey(User)
message = models.ForeignKey(Message)
This structure is using the power of ManyToMany with a custom through Model, check this out, it very mighty: https://docs.djangoproject.com/en/1.11/ref/models/fields/#django.db.models.ManyToManyField.through.
tldr: we want every receiver to have a read status, so we modeled this in a separate object
Longer version: we utilize the power of a custom ManyToMany through model to have a separate read status for every receiver. This means we need to change some parts of our code to work for the many to many structure, e.g. if we want to know if a message was read by all receivers:
def did_all_receiver_read_the_message(message)
unread_count = my_message.receive_users.filter(is_read__isnull=True).count()
if unread_count > 0:
return True
return False
if we want to know if a specific user read a specific message:
def did_user_read_this_message(user, message)
receiver = message.receive_users.get(receiver=user)
return bool(receiver.is_read)
3. Conversations + Messages + Participants
This is something that would exceed my time limit but some short hints:
Conversation holds everything together
Message is written by a Participant and holds a created timestamp
Participant allows access to a conversation and links a User to the Conversation object
the Participant holds a last_read timestamp with can be used to calculate if a message was read or not using the messages created timestamps (-> annoyingly complex part & milliseconds are important)
Everything else pbly would need to be adapted to your specific business needs. This scenario is pbly the most flexible but it's a lot of work (based on personal experience) and adds quite a bit of complexity to your architecture - I only recommend this if it's really really needed ^^.
Disclaimer:
This could be an overall structure, most design decisions I made for the examples are based on assumptions, I could only mentioned some or the text would to long, but feel free to ask.
Please excuse any typos and errors, I didn't had the chance to run the code.
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 using appengine with python 2.7 and webapp2 framework. I am not using ndb.model.
I have the following model:
class Story(db.Model);
name = db.StringProperty()
class UserProfile(db.Model):
name = db.StringProperty()
user = db.UserProperty()
class Tracking(db.Model):
user_profile = db.ReferenceProperty(UserProfile)
story = db.ReferenceProperty(Story)
upvoted = db.BooleanProperty()
flagged = db.BoolenProperty()
A user can upvote and/or flag a story but only once. Hence I came up with the above model.
Now when a user clicks on the upvote link, on the database I try to see if the user has not already voted it, hence I do try to do the following:
get the user instance with his id as up = db.get(db.Key.from_path('UserProfile', uid))
then get the story instance as follows s_ins = db.get(db.Key.from_path('Story', uid))
Now it is the turn to check if a Tracking based on these two exist, if yes then don't allow voting, else allow him to vote and update the Tracking instance.
What is the most convenient way to fetch a Tracking instance given an id(db.key().id()) of user_profile and story?
What is the most convenient way to save a Tracking model having given a user profile id and an story id?
Is there a better way to implement tracking?
You can try tracking using lists of keys versus having a separate entry for track/user/story:
class Story(db.Model);
name = db.StringProperty()
class UserProfile(db.Model):
name = db.StringProperty()
user = db.UserProperty()
class Tracking(db.Model):
story = db.ReferenceProperty(Story)
upvoted = db.ListProperty(db.Key)
flagged = db.ListProperty(db.Key)
So when you want to see if a user upvoted for a given story:
Tracking.all().filter('story =', db.Key.from_path('Story', uid)).filter('upvoted =', db.Key.from_path('UserProfile', uid)).get(keys_only=True)
Now the only problem here is the size of the upvoted/flagged lists can't grow too large (I think the limit is 5000), so you'd have to make a class to manage this (that is, when adding to the upvoted/flagged lists, detect if X entries exists, and if so, start a new tracking object to hold additional values). You will also have to make this transactional and with HR you have a 1 write per second threshold. This may or may not be an issue depending on your expected use case. A way around the write threshold would be to implement upvotes/flags using pull-queues and to have a cron job that pulls and batch updates tracking objects as needed.
This method has its pros/cons. The most obvious cons are the ones I just listed. The pros, however, may be worth it. You can get a full list of users who upvoted/flagged a story from a single list (or multiple depending on how popular the story is). You can get a full list of users with a lot fewer queries to the datastore. This method should also take less storage, index, and metadata space. Additionally, adding a user to a tracking object will be cheaper, instead of writing a new object + 2 writes for each property, you would just be charged 1 write for the object + 2 writes for the entry to the list (9 vs 3 writes for adding users to a pre-existing tracked story, or 9 vs 7 for untracked stories)
What you propose sounds reasonable.
Don't use the app engine generated key for Tracking. Because the combination of story/user should be unique, create your own key as a combination of the story/user. Something like
tracking = Tracking.get_or_insert(str(story.id) + "-" + str(user.id), **params)
If you know the story/user, then you can always fetch the tracking by key name.
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