Using aggregation in Django with - django

Following is my model:
class Story(models.Model):
author = models.ForeignKey(User, related_name='author_id', default=1)
source = models.ForeignKey(User, related_name='source_id', default=2)
User is the django provided model.
Now I need to find out number of articles authored and sourced by each user.
I thought of using story_set with user object:
res = User.objects.annotate(Count('story_set'))
However, there are two columns in story referencing User. Hence, Django won't know which one to use?
Can anyone help me on this?

story_set doesn't exist one way or another. That would've been the default related_name if you hadn't provided one. Since you did, you have to use those.
res = User.objects.annotate(Count('author_id'))
OR
res = User.objects.annotate(Count('source_id'))
So, that's how Django knows the difference.
FYI: if you had used the default (so you accessed stories via .story_set.all(), you don't use the "_set" part in queries. It would just be Count('story').

the reason django makes you specify a related_name is exactly for this reason. you want Count('author_id') instead of Count('story_set') (and you should probably give these better names, e.g. author_set)

res = User.objects.annotate(num_authored=Count('author_id')).annotate(num_sourced=Count('source_id'))
res[0].num_authored
res[0].num_sourced
If you want both the number of authored and number of sourced articles in one query. You might want more appropriate related names, like "authored" and "sourced".

Related

Request changes/additions to data in another app

To make a long story short, I am very grateful for hints on how I can accomplish the following. I have an app A that I don't want to change. I have an app B that needs to select data from A or to request data to be added/changed if necessary. Think of B as an app to suggest data that should end in A only after review/approval. By itself, B is pretty useless. Furthermore, a significant amount of what B's users will enter needs to be rejected. That's why I want A to be protected so to say.
# in app A
class Some_A_Model(models.Model): #e.g., think artist
some_value = models.TextField()
# in app B
class MyCustomField(models.ForeignKey):
...
class Some_B_Model(models.Model): # e.g., think personal preference
best_A = MyCustomField('Some_A_Model')
worst_A = MyCustomField('Some_A_Model')
how_many_times_I_like_the one_better_than_the_other = models.FloatField()
class Mediator(models.Model):
# already exists: generic foreign key
content_type = models.ForeignKey(
ContentType,
on_delete=models.CASCADE
)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey(
'content_type',
'object_id'
)
#does not yet exist or needs to be changed:
add_or_change = PickledObjectField()
Django should create a form for Some_B_Model where I can select instances of Some_A_Model for best_A and worst_A, respectively; if, however, my intended best_A is not yet in A's database, I want to be able to request this item to be added. And if I find worst_A is present but has a typo, I want to be able to request this item to be corrected. An editor should be required to review/edit the data entered in B and either reject or release all the associated changes to A's database as an atomic transaction. I don't want any garbage in A and refrain from adding some status field to track what is considered valid, requiring filtering all the time. If it's in A, it must be good.
I figured I need to define a MyCustomField, which could be a customized ForeignKey. In addition, I need some intermediate model ('mediator' maybe?) that MyCustomField would actually be pointing to and that can hold a (generic) ForeignKey to the item I selected, and a pickled instance of the item I would like to see added to A's database (e.g., a pickled, unsaved instance of Some_A_model), or both to request a change. Note that I consider using PickledObjectField from 'django-picklefield', but this is not a must.
As there is only some documentation on custom model fields but not on the further steps regarding form fields and widgets, it seems I have to dig through django's source to find out how to tie my intended functionality into its magic. That's where I am hoping for some comments and hints. Does my plan sound reasonable to you? Is this a known pattern, and if so, what is it called? Maybe someone has already done this or there is a plugin I could look into? What alternatives would you consider?
Many thanks in advance!
Best regards

How do you perform a filtered database lookup in views?

I'm working with a database in Django. I'm familiar with the Django database API but am wondering how to interact with the data base inside views.py. Here's the relevant model:
class SlotFilling(models.Model):
originator = models.CharField(max_length=20)
empty_slot = models.BooleanField(default=False)
I'm trying to write an If statment in my program to check if empty_slot is True for a given originator. I'm thinking I might be able to use filter() to accomplish this. Any experience with the most efficient way to implement this?
If you are looking to query for originator all empty slots you can do something as following
SlotFilling.objects.filter(empty_slot=True, originator='someoriginator')
#comment code
Assuming originator is unique
originator_slot = SlotFlling.objects.get(originator='originator')
slot_value = originator_slot.empty_slot
You can use filter instead if originator is not unique, which would list you back all rows for particular originator
originator_slots = SlotFlling.objects.filter(originator='originator')
for originator_slot in originator_slots:
print originator_slot.empty_slot
Also please check out retrieving objects in DB API documentation as saying that you are familiar with it would be big overstatement :)

dynamic condition for relationship(sqlalchemy) in Flask-Admin

I'm using sqlalchemy and have two models, Article and Tag, it's a many-to-many relation.
When I add articles using Flask-Admin, I want just part of tags available (related on user permission) instead of all tags.
any idea? Thanks
Probably the best way to do this is to use dynamic relationship loaders. Simply use lazy='dynamic' in your relationship definition:
posts = relationship(Post, lazy="dynamic")
This returns you a query object instead of a collection of objects, so you can then query it directly:
posts = jack.posts.filter(Post.headline=='this is a post')
You could also achieve what you want with discriminator columns or something, but that is likely overkill.
sounds like you need ModelView.get_query:
class MyView(ModelView):
def get_query(self,*args,**kwargs):
return super(MyView,self).get_query(*args,**kwargs).filter_by(current_user.can_view=True)

In Django, how can I calculate or update certain model fields BEFORE any validation happens?

So I'm new to Django...
First some background on how we do things now. We have a custom php system but I am constructing an improved inventory management system in django using only the admin interface. We store part numbers, and it is essential that we do not store duplicates. Part numbers can sometimes be entered with hypens, periods, spaces, etc. We need to be sure that duplicate parts are not added no matter what kind of formatting is entered. With our existing non-django system, we use a regex to strip anything from the string that is not a-zA-Z0-9. The actual entered part number is persisted, and the cleaned number is persisted to the db as well. Then when someone is adding a new part or even searching for a part, this cleaned version of the part number helps to avoid this ambiguity. We do the same for the manufacturer name.
My way of emulating this in django was to add the part_number_clean field along with the part_number field to the model. Then I overrode the save method to calculate the clean part number like so (manufacturer as well):
def save(self, *args, **kwargs):
self.manufacturer_clean = re.sub(r'[^a-zA-Z0-9]', '', self.manufacturer).lower()
self.part_number_clean = re.sub(r'[^a-zA-Z0-9]', '', self.part_number).lower()
super(CatalogProduct, self).save(*args, **kwargs)
The problem is, I need to unique on a combination of part number and manufacturer:
class Meta:
unique_together = ('part_number_clean ', 'manufacturer_clean ')
When I try to save a duplicate record, I get a database integrity violation. So it seems like django is evaluating the unique fields before the save function is called (which makes sense). I just need to know how or which method I should override to calculate these fields BEFORE any validation.
Additionally, I am interested in adding a third field to the unique_together mix that may or may not be filled out. If it is not filled it will just have an empty default value. I hope this will not cause any issues.
It would also be great if when the user tabbed-out of the manufacturer and part number fields, and both were not empty, some js would see if that product exists already, and offer the user the option to click a button and be whisked away to that record, before they waste their time filling out the rest of the data only to find that it already exists. I'm guessing this lies way outside the realm of the admin interface without serious hacking. Is there any way to somehow integrate this with the admin interface? Its working great for me up till now...
I figured it out. I'm posting the answer for anyone else that is curious. This was actually very simple in the end to implement in the model. All one needs to do is implement (override?) the clean() method of the model. In the method, I calculate and set my special fields, then be sure to call self.validate_unique() after. Works like a charm! No need to raise any exceptions, the form will display the error on top perfectly. Doing this in the save method will not work, as the exception cannot be thrown by your code or django at that point. Here is the code:
class CatalogProduct(models.Model):
manufacturer = models.CharField(max_length=100)
manufacturer_clean = models.CharField('Manufacturer',max_length=100,blank=True,editable=False)
part_number = models.CharField(max_length=100)
part_number_clean = models.CharField('Part number',max_length=100,blank=True,editable=False)
def clean(self):
# Calculate manufacturer_clean and part_number_clean
self.manufacturer_clean = re.sub(r'[^a-zA-Z0-9]', '', self.manufacturer).lower()
self.part_number_clean = re.sub(r'[^a-zA-Z0-9]', '', self.part_number).lower()
self.validate_unique()
The model is only responsible for describing data and how that data should be represented between your Python and database environment. It's because of this atomic role that models don't care about validation and what you've just went in there and introduced it.
You need a model form. It can clean the manufacturer and part number and also ensure that uniqueness constraints are satisfied as part of the validation process.

Django: Querying comments based on object field

I've been using the built-in Django comments system which has been working great. On a particular page I need to list the latest X comments which I've just been fetching with:
latest_comments =
Comment.objects.filter(is_public=True, is_removed=False)
.order_by('submit_date').reverse()[:5]
However I've now introduced a Boolean field 'published' into the parent object of the comments, and I want to include that in the query above. I've tried using the content_type and object_pk fields but I'm not really getting anywhere. Normally you'd do something like:
Comment.objects.filter(blogPost__published=True)
But as it is not stored like that I am not sure how to proceed.
posts_ids = BlogPost.objects.filter(is_published=True).values_list('id', flat=True) #return [3,4,5,...]
ctype = ContentType.objects.get_for_model(BlogPost)
latest_comments = Comment.objects.filter(is_public=True, is_removed=False, content_type=ctype, content_object__in=posts_ids).order_by('-submit_date')[:5]
Comments use GenericForeignKey to store the relation to parent object. Because of the way generic relations work related lookups using __<field> syntax are not supported.
You can accomplish the desired behaviour using the 'in' lookup, however it'll require lot of comparisons when there'll be a lot of BlogPosts.
ids = BlogPost.objects.filter(published=True).values_list('id', flat=True) # Get list of ids, you would probably want to limit number of items returned here
content_type = ContentType.objects.get_for_model(BlogPost) # Becasue we filter only comments for BlogPost
latest_comments = Comment.objects.filter(content_type=content_type, object_pk__in=ids, is_public=True, is_removed=False, ).order_by('submit_date').reverse()[:5]
See the Comment model doc for the description of all fields.
You just cannot do that in one query. Comments use GenericForeignKey. Documentation says:
Due to the way GenericForeignKey is implemented, you cannot use such
fields directly with filters (filter() and exclude(), for example) via
the database API.