Autoincrement with MongoEngine - python

I'm developing a blog engine with Flask and MongoEngine, and I need sequential IDs for my posts.
I need MongoEngine to create a new ID for each new post, so I was thinking of doing something like this:
class Post(Document):
title = StringField(required=True)
content = StringField(required=True)
published_at = datetime.utcnow()
id = Post.objects.count() + 1
Will this work? is there a better way to do this?

Firstly, you need to understand why you need incremental id's? What do they solve?
Theres no native solution in mongoDB - please read: http://www.mongodb.org/display/DOCS/How+to+Make+an+Auto+Incrementing+Field
As you already have a unique identifier with the pk of the Post, why not use that?
Finally, if I haven't dissuaded you from folly, there is a SequenceField in mongoengine that handles incrementing for you.

Edit: This is an incorrect solution, as others pointed out that this approach causes a race condition. I have only left it here so others would know why this is bad. (multiple clients can access this same object and increment it, resulting in inconsistent results).
Old answer:
I figured it out.
The Post class looks like this:
class Post(Document):
title = StringField(required=True)
content = StringField(required=True)
published_at = datetime.utcnow()
ID = IntField(min_value=1)
And in the function that inserts the post, I count the available records and then increment them by 1, like so:
def create_post(title, content):
Post(title=title, content=content, ID=Post.objects.count() + 1).save()

you can use mongoengine.signals and the post_init for the sake of auto incrementing a field. haven't tested it btw.

In my case I needed to create a sequential number for each invoice generated at the POS.
I used class mongoengine.fields.SequenceField
class Model(Document):
.....
sequenceSale = db.SequenceField(required=False) #(default int)
NOTE
in case the counter is defined in the abstract document, it will be common to all inherited documents and the default sequence name will be the class name of the abstract document.
More about mongoengine.fields.SequenceField

Related

Django: join two table on foreign key to third table?

I have three models
class A(Model):
...
class B(Model):
id = IntegerField()
a = ForeignKey(A)
class C(Model):
id = IntegerField()
a = ForeignKey(A)
I want get the pairs of (B.id, C.id), for which B.a==C.a. How do I make that join using the django orm?
Django allows you to reverse the lookup in much the same way that you can use do a forward lookup using __:
It works backwards, too. To refer to a “reverse” relationship, just use the lowercase name of the model.
This example retrieves all Blog objects which have at least one Entry whose headline contains 'Lennon':
Blog.objects.filter(entry__headline__contains='Lennon')
I think you can do something like this, with #Daniel Roseman's caveat about the type of result set that you will get back.
ids = B.objects.prefetch_related('a', 'a__c').values_list('id', 'a__c__id')
The prefetch related will help with performance in older versions of django if memory serves.

How to get an extra count field with Django ORM?

My Django Models are like this:
class User(models.Model):
username = models.CharField(max_length=32)
class Message(models.Model):
content = models.TextField()
class UserMessageRel(models.Model):
user = models.ForeignKey(User)
message = models.ForeignKey(Message)
is_read = models.BooleanField()
Now I want to get all messages, for each message, I need to know how many users that received it has read it.
The naive way to do it is:
msgs = Message.objects.all()
messages = []
for msg in msgs:
reads = UserMessageRel.objects.filter(message=msg, is_read=True).count()
messages.append((msg, reads))
But this is very inefficient, with a SQL query to get the number of reads for each message.
I am not sure if this can be done with annotations or aggregations in ORM?
What I want is something like this:
msgs_with_reads = Message.objects.all().annotate(
number_of_reads=Count("user_message_rel_with_is_read_true"))
which can be translated into one nice SQL query.
Is this achievable?
I'm interpreting your question to be that you want to improve query time for this count. Unfortunately, with the current setup, a full table scan is necessary. There are ways to improve it, the easiest being indexing. You could add an index on the Message id column in UserMessageRel, which would speed up the read time (at the cost of space, of course). The most readable way to access this count though, is Pieter's answer.
You can do a related lookup from the Message object, I would put a helper function on the Message model like this, then you would be able to call the function from the object.
def get_read_count(self):
return self.usermessagerel_set.filter(is_read=True).count()
message_obj.get_read_count()
I didn't find a way to use Django ORM to generate one SQL query for my requirement, but the following code can generate 2 queries:
messages = Message.objects.all()
messageReads = UserMessageRel.objects.filter(isRead=True).
values("message_id").annotate(cnt=Count("user"))
Then I can map the messages with their read count in python.
This solution is good enough for me.

Unsure about what type of Django form I should use

I have an Author model with 200k instances in a MySQL database. I want to have the user search for an Author's unique ID (which is a string) and then select an ID which will then produce a table and small graphic about that author.
Do I want to use a charfield model form? Also, is there a built in search function?
I don't think Django has a builtin function for searching. You will have to use one of its extensions for this purpose (django-search or haystack).
They may seem too complicated for your case so I would go with simplier solution (and I would give up using form):
from django.views.generic import ListView
from django.db.models import Q
from .models import Author
def SearchAuthorView(ListView):
def get_queryset(self):
name = self.request.GET['author_name']
name_tokens = name.split(' ')
matched_authors = []
authors = Author.objects.all()
for author in authors:
for name_token in author.name.split(' '):
if name_token in name_tokens:
matched_authors.append(author)
break
return matched_authors
With 200k you may hit performance problems so if you do, you should use an optimized, raw MySql query.
You may also like to order your results somehow. For example give lastname's match a bigger priority than firstname's match.
Honestly I don't understand the question. You have a table called Author with 200k instances and you want to have possibility to find one of them. This can be done by simply function in views.py
def search_author(request, author_id):
author = Author.objects.get(id=author_id)
return render_to_response('your/path/to/template', {'author': author}, RequestContext(request))
Then in your template you just simply display the informations:
<div class="author">
<p>{{author.name}}</p>
<p>{{author.image}}</p>
</div>
Of course if your models.py looks like this:
class Author(models.Model):
name = models.CharField(max_number=100)
image ....

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

Django Foreign key queries

In the following model:
class header(models.Model):
title = models.CharField(max_length = 255)
created_by = models.CharField(max_length = 255)
def __unicode__(self):
return self.id()
class criteria(models.Model):
details = models.CharField(max_length = 255)
headerid = models.ForeignKey(header)
def __unicode__(self):
return self.id()
class options(models.Model):
opt_details = models.CharField(max_length = 255)
headerid = models.ForeignKey(header)
def __unicode__(self):
return self.id()
If there is a row in the database for table header as
Id=1, title=value-mart , createdby=CEO
How do I query criteria and options tables to get all the values related to header table
id=1
Also can some one please suggest a good link for queries examples.
Ironfroggy is right, but there is another more obvious way to get the relevant options and criteria objects. Django automatically creates a 'reverse relation' for every foreign key pointing at a model, and that is usually the name of the related model plus _set. So:
mycriteria.options_set.all()
mycriteria.header_set.all()
will give you all the options and header objects related to a criteria object mycriteria.
Also, a note on style: as ironfroggy pointed out, you shouldn't use id in the foreign key fields, but also you should use Capitalised style for your model classes, so you can see a difference between the class Criteria and a particular instance criteria.
In terms of links, the Django documentation is excellent and explains all of this.
First of all, don't use id in the names, because it is confusing. That field isn't the ID, it is the object itself. (If you have a field ref it automatically creates a field ref_id)
options.objects.filter(header=a_header)
You query it like any value, where some header instance is the value you are filtering on.
Sounds like you are looking for Following relationships "backward".
You can get the header object you want to filter by, and use something like
obj = Header.objects.get(title="value-mart", "createdby=CEO")
obj.criteria_set.all()
Look at the documentation for more detailed info
I would suggest trying to us a coding style and naming convention that is more like you see in the Django documentation for Models. Something more like this:
class Header(models.Model):
...
class Criteria(models.Model):
details = model.CharField(max_length=255)
header = models.ForeignKey(Header)
And then query them as needed:
# find Criteria for a given header
value_mart = Header.objects.get(id=1)
# ... via an instance of Header.
value_mart.criteria_set.all()
# ... or with a filter().
Criteria.objects.filter(header=value_mart)
Criteria.objects.filter(header_id=1)
The documentation for many-to-one relationships also references a usage example.

Categories