I have a simple question but multiple google searches left me without a nice solution. Currently I am doing the following:
allowed_categories = self.allowed_view.all().difference(self.not_allowed_view.all())
users = []
for cat in allowed_categories:
for member in cat.members.all():
users.append(member)
return users
I have a ManyToManyField to Objects that also have a ManyToManyField for instances of Users. In the code above, I am trying to get all the users from all those categories and get a list of all Users.
Later I would like the same in a method allowed_to_view(self, user_instance) but that's for later.
How would I achieve this using Django ORM without using nested for-loops?
[edit]
My models are as follows:
class RestrictedView(models.Model):
allowed_view = models.ManyToManyField(Category)
not_allowed_view = models.ManyToManyField(Category)
class Category(models.Model):
name = models.CharField(max_length=30)
members = models.ManyToManyField(User)
So, I've made the following one-liner with only one query towards the database. It took me some time...
users = User.objects.filter(pk__in=self.allowed_view.all().values("users").difference(self.not_allowed_view.all().values("users")))
This gives me a nice queryset with only the users that are in the allowed_view and explicitly not in the not_allowed_view.
Without seeing you database structure / models.py file its hard to say, but you can do a search on member objects like so:
members_queryset = Member.objects.filter(
category = <allowed categories>,
...
)
users += list(members.all())
Related
Hello folks Im new to Django(I have just the finished the tutorial) but I think i understand the basic concepts of it .Im writing here because Im trying to do something "difficult" for my current experience with django and searching the internet didnt give me a solution .What im trying to do is to create a dynamic model based on the number of entries of another model .To be more exact lets say i got the following model :
class criteria(models.Model):
criteria_text = models.CharField(max_length=200)
monotonicity = models.CharField(max_length=1,choices=(('+','ASCEDING'),('-','DESCENDING')),default='+',verbose_name='Monotonicity')
worst = models.IntegerField(default=0)
best = models.IntegerField(default=0)
What i want to do is create all the criteria models instances i want through the django admin panel and then query for all the creteria_text instances in the database and make a model with an attribute for every criteria_text instance.
So lets say I add the following criteria to the database(these are criteria_text attributes of criteria objects: Color,Weight,Price .
I want to end up with a model like this :
class Alternative(models.Model):
Color = models.IntegerField(default=0)
Weight = models.IntegerField(default=0)
Price = models.IntegerField(default=0)
The thing is that in my application this one has to happen a lot of times so i cannot make model each time someone adds an Alternative based on different criteria .
After searching i found that i can define dynamic models using the following format :
attrs = {
'name': models.CharField(max_length=32),
'__module__': 'myapp.models'
}
Animal = type("Animal", (models.Model,), attrs)
So the question is how can I define "attrs" based on a query that gets all the criteria in the database ?Can i define a relationship of this dynamic model with another model ? Also the models already created should be updated if a user adds more criteria .
Is something like this possible ?
If so please show me the path .
I don't think defining dynamic models is a good solution here (or anywhere, really). Rather, you need a relationship that can have as many items as there are criteria instances.
It might be something like this:
class Alternative(models.Model):
name = models.CharField(...)
choices = models.ManyToManyField("Criteria", through="AlternativeChoice")
class AlternativeChoice(models.Model):
alternative = models.ForeignKey('Alternative')
criteria = models.ForeignKey('Criteria')
value = models.IntegerField(default=0)
The real logic will belong in the form, where you will need to create options for each criteria entry, and validate the AlternativeChoice dependent on the related criteria.
I want to build an webapp like Quora or Medium, where a user can follow users or some topics.
eg: userA is following (userB, userC, tag-Health, tag-Finance).
These are the models:
class Relationship(models.Model):
user = AutoOneToOneField('auth.user')
follows_user = models.ManyToManyField('Relationship', related_name='followed_by')
follows_tag = models.ManyToManyField(Tag)
class Activity(models.Model):
actor_type = models.ForeignKey(ContentType, related_name='actor_type_activities')
actor_id = models.PositiveIntegerField()
actor = GenericForeignKey('actor_type', 'actor_id')
verb = models.CharField(max_length=10)
target_type = models.ForeignKey(ContentType, related_name='target_type_activities')
target_id = models.PositiveIntegerField()
target = GenericForeignKey('target_type', 'target_id')
tags = models.ManyToManyField(Tag)
Now, this would give the following list:
following_user = userA.relationship.follows_user.all()
following_user
[<Relationship: userB>, <Relationship: userC>]
following_tag = userA.relationship.follows_tag.all()
following_tag
[<Tag: tag-job>, <Tag: tag-finance>]
To filter I tried this way:
Activity.objects.filter(Q(actor__in=following_user) | Q(tags__in=following_tag))
But since actor is a GenericForeignKey I am getting an error:
FieldError: Field 'actor' does not generate an automatic reverse relation and therefore cannot be used for reverse querying. If it is a GenericForeignKey, consider adding a GenericRelation.
How can I filter the activities that will be unique, with the list of users and list of tags that the user is following? To be specific, how will I filter GenericForeignKey with the list of the objects to get the activities of the following users.
You should just filter by ids.
First get ids of objects you want to filter on
following_user = userA.relationship.follows_user.all().values_list('id', flat=True)
following_tag = userA.relationship.follows_tag.all()
Also you will need to filter on actor_type. It can be done like this for example.
actor_type = ContentType.objects.get_for_model(userA.__class__)
Or as #Todor suggested in comments. Because get_for_model accepts both model class and model instance
actor_type = ContentType.objects.get_for_model(userA)
And than you can just filter like this.
Activity.objects.filter(Q(actor_id__in=following_user, actor_type=actor_type) | Q(tags__in=following_tag))
What the docs are suggesting is not a bad thing.
The problem is that when you are creating Activities you are using auth.User as an actor, therefore you can't add GenericRelation to auth.User (well maybe you can by monkey-patching it, but that's not a good idea).
So what you can do?
#Sardorbek Imomaliev solution is very good, and you can make it even better if you put all this logic into a custom QuerySet class. (the idea is to achieve DRY-ness and reausability)
class ActivityQuerySet(models.QuerySet):
def for_user(self, user):
return self.filter(
models.Q(
actor_type=ContentType.objects.get_for_model(user),
actor_id__in=user.relationship.follows_user.values_list('id', flat=True)
)|models.Q(
tags__in=user.relationship.follows_tag.all()
)
)
class Activity(models.Model):
#..
objects = ActivityQuerySet.as_manager()
#usage
user_feed = Activity.objects.for_user(request.user)
but is there anything else?
1. Do you really need GenericForeignKey for actor? I don't know your business logic, so probably you do, but using just a regular FK for actor (just like for the tags) will make it possible to do staff like actor__in=users_following.
2. Did you check if there isn't an app for that? One example for a package already solving your problem is django-activity-steam check on it.
3. IF you don't use auth.User as an actor you can do exactly what the docs suggest -> adding a GenericRelation field. In fact, your Relationship class is suitable for this purpose, but I would really rename it to something like UserProfile or at least UserRelation. Consider we have renamed Relation to UserProfile and we create new Activities using userprofile instead. The idea is:
class UserProfile(models.Model):
user = AutoOneToOneField('auth.user')
follows_user = models.ManyToManyField('UserProfile', related_name='followed_by')
follows_tag = models.ManyToManyField(Tag)
activies_as_actor = GenericRelation('Activity',
content_type_field='actor_type',
object_id_field='actor_id',
related_query_name='userprofile'
)
class ActivityQuerySet(models.QuerySet):
def for_userprofile(self, userprofile):
return self.filter(
models.Q(
userprofile__in=userprofile.follows_user.all()
)|models.Q(
tags__in=userprofile.relationship.follows_tag.all()
)
)
class Activity(models.Model):
#..
objects = ActivityQuerySet.as_manager()
#usage
#1st when you create activity use UserProfile
Activity.objects.create(actor=request.user.userprofile, ...)
#2nd when you fetch.
#Check how `for_userprofile` is implemented this time
Activity.objects.for_userprofile(request.user.userprofile)
As stated in the documentation:
Due to the way GenericForeignKey is implemented, you cannot use such fields directly with filters (filter() and exclude(), for example) via the database API. Because a GenericForeignKey isn’t a normal field object, these examples will not work:
You could follow what the error message is telling you, I think you'll have to add a GenericRelation relation to do that. I do not have experience doing that, and I'd have to study it but...
Personally I think this solution is too complex to what you're trying to achieve. If only the user model can follow a tag or authors, why not include a ManyToManyField on it. It would be something like this:
class Person(models.Model):
user = models.ForeignKey(User)
follow_tag = models.ManyToManyField('Tag')
follow_author = models.ManyToManyField('Author')
You could query all followed tag activities per Person like this:
Activity.objects.filter(tags__in=person.follow_tag.all())
And you could search 'persons' following a tag like this:
Person.objects.filter(follow_tag__in=[<tag_ids>])
The same would apply to authors and you could use querysets to do OR, AND, etc.. on your queries.
If you want more models to be able to follow a tag or author, say a System, maybe you could create a Following model that does the same thing Person is doing and then you could add a ForeignKey to Follow both in Person and System
Note that I'm using this Person to meet this recomendation.
You can query seperately for both usrs and tags and then combine them both to get what you are looking for. Please do something like below and let me know if this works..
usrs = Activity.objects.filter(actor__in=following_user)
tags = Activity.objects.filter(tags__in=following_tag)
result = usrs | tags
You can use annotate to join the two primary keys as a single string then use that to filter your queryset.
from django.db.models import Value, TextField
from django.db.models.functions import Concat
following_actor = [
# actor_type, actor
(1, 100),
(2, 102),
]
searchable_keys = [str(at) + "__" + str(actor) for at, actor in following_actor]
result = MultiKey.objects.annotate(key=Concat('actor_type', Value('__'), 'actor_id',
output_field=TextField()))\
.filter(Q(key__in=searchable_keys) | Q(tags__in=following_tag))
models.py:
class Ingredient(models.Model):
_est_param = None
param = models.ManyToManyField(Establishment, blank=True, null=True, related_name='+', through='IngredientParam')
def est_param(self, establishment):
if not self._est_param:
self._est_param, created = self.ingredientparam_set\
.get_or_create(establishment=establishment)
return self._est_param
class IngredientParam(models.Model):
#ingredient params
active = models.BooleanField(default=False)
ingredient = models.ForeignKey(Ingredient)
establishment = models.ForeignKey(Establishment)
I need to fetch all Ingredient with parametrs for Establishment. First I fetch Ingredients.objects.all() and use all params like Ingredients.objects.all()[0].est_param(establishment).active. How I can use django 1.4 prefetch_related to make less sql queries? May be I can use other way to store individual Establishment properties for Ingredient?
Django 1.7 adds the Prefetch object you can put into prefetch_related. It allows you to specify a queryset which should provide the filtering. I'm having some problems with it at the moment for getting a singular (latest) entry from a list, but it seems to work very well when trying to get all the related entries.
You could also checkout django-prefetch which is part of this question which does not seem a duplicate of this question because of the vastly different wording.
The following code would fetch all the ingredients and their parameters in 2 queries:
ingredients = Ingredients.objects.all().prefetch_related('ingredientparam_set')
You could then access the parameters you're interested in without further database queries.
My problem is that I don't really know how to put my design in terms of a relational db.
I have a class 'Feat', which should contain several other classes, namely lists of 'Stat's, 'Skill's and 'Attribute's. The exact number of those depends on the instance of 'Feat', so I can't just define a static number of fields.
In normal Python, I'd simply use lists:
class Feat():
name
desc
att_effects = []
skill_effects = []
stat_effects = []
I tried to replicate this in Django using the Many-to-Many relationship, but that didn't quite work out.
class Attribute_Name(models.Model):
name = models.CharField(max_length=100)
desc = models.TextField()
def __unicode__(self):
return str(self.name)
...
class Attribute(models.Model):
name = models.ForeignKey(Attribute_Name)
value = models.IntegerField()
def __unicode__(self):
return "%s: %s" % (self.name, self.value)
...
class Feat(models.Model):
name = models.CharField(max_length=100)
desc = models.TextField()
att_effects = models.ManyToManyField(Attribute)
skill_effects = models.ManyToManyField(Skill)
stat_effects = models.ManyToManyField(Stat)
With these models, I could create new 'Attribute's when creating a new 'Feat' in the admin interface. However, I would always see all existing 'Attribute's, not just those pertaining to my new 'Feat'. Is there a way to restrict the view to only those created along with the new 'Feat'? Something like a back-link to the 'Feat' which created the 'Attribute'?
Basically, when I create or edit a 'Feat', I should only be able to use 'Attribute's created from the page of this instance or add a new 'Attribute' instance. I don't want 'Attribute's belonging to another 'Feat' (or another unrelated class) to show up.
Note that I don't want to add a field to the 'Attribute' class, since it should also be used along with other classes (eg 'Character')
I'd also like to do this within the admin interface, since it saves me quite a bit of work.
On a related note: Is there a way to leave a Many-to-Many field empty? Or should I just create a magic 'empty' value and set it as the default?
"there a way to restrict the view to only those created along with the new 'Feat'? Something like a back-link to the 'Feat' which created the 'Attribute'?"
Yes: use the queryset feature of ModelAdmin as described in this question
this is a model of the view table.
class QryDescChar(models.Model):
iid_id = models.IntegerField()
cid_id = models.IntegerField()
cs = models.CharField(max_length=10)
cid = models.IntegerField()
charname = models.CharField(max_length=50)
class Meta:
db_table = u'qry_desc_char'
this is the SQL i use to create the table
CREATE VIEW qry_desc_char as
SELECT
tbl_desc.iid_id,
tbl_desc.cid_id,
tbl_desc.cs,
tbl_char.cid,
tbl_char.charname
FROM tbl_desC,tbl_char
WHERE tbl_desc.cid_id = tbl_char.cid;
i dont know if i need a function in models or views or both. i want to get a list of objects from that database to display it. This might be easy but im new at Django and python so i having some problems
Django 1.1 brought in a new feature that you might find useful. You should be able to do something like:
class QryDescChar(models.Model):
iid_id = models.IntegerField()
cid_id = models.IntegerField()
cs = models.CharField(max_length=10)
cid = models.IntegerField()
charname = models.CharField(max_length=50)
class Meta:
db_table = u'qry_desc_char'
managed = False
The documentation for the managed Meta class option is here. A relevant quote:
If False, no database table creation
or deletion operations will be
performed for this model. This is
useful if the model represents an
existing table or a database view that
has been created by some other means.
This is the only difference when
managed is False. All other aspects of
model handling are exactly the same as
normal.
Once that is done, you should be able to use your model normally. To get a list of objects you'd do something like:
qry_desc_char_list = QryDescChar.objects.all()
To actually get the list into your template you might want to look at generic views, specifically the object_list view.
If your RDBMS lets you create writable views and the view you create has the exact structure than the table Django would create I guess that should work directly.
(This is an old question, but is an area that still trips people up and is still highly relevant to anyone using Django with a pre-existing, normalized schema.)
In your SELECT statement you will need to add a numeric "id" because Django expects one, even on an unmanaged model. You can use the row_number() window function to accomplish this if there isn't a guaranteed unique integer value on the row somewhere (and with views this is often the case).
In this case I'm using an ORDER BY clause with the window function, but you can do anything that's valid, and while you're at it you may as well use a clause that's useful to you in some way. Just make sure you do not try to use Django ORM dot references to relations because they look for the "id" column by default, and yours are fake.
Additionally I would consider renaming my output columns to something more meaningful if you're going to use it within an object. With those changes in place the query would look more like (of course, substitute your own terms for the "AS" clauses):
CREATE VIEW qry_desc_char as
SELECT
row_number() OVER (ORDER BY tbl_char.cid) AS id,
tbl_desc.iid_id AS iid_id,
tbl_desc.cid_id AS cid_id,
tbl_desc.cs AS a_better_name,
tbl_char.cid AS something_descriptive,
tbl_char.charname AS name
FROM tbl_desc,tbl_char
WHERE tbl_desc.cid_id = tbl_char.cid;
Once that is done, in Django your model could look like this:
class QryDescChar(models.Model):
iid_id = models.ForeignKey('WhateverIidIs', related_name='+',
db_column='iid_id', on_delete=models.DO_NOTHING)
cid_id = models.ForeignKey('WhateverCidIs', related_name='+',
db_column='cid_id', on_delete=models.DO_NOTHING)
a_better_name = models.CharField(max_length=10)
something_descriptive = models.IntegerField()
name = models.CharField(max_length=50)
class Meta:
managed = False
db_table = 'qry_desc_char'
You don't need the "_id" part on the end of the id column names, because you can declare the column name on the Django model with something more descriptive using the "db_column" argument as I did above (but here I only it to prevent Django from adding another "_id" to the end of cid_id and iid_id -- which added zero semantic value to your code). Also, note the "on_delete" argument. Django does its own thing when it comes to cascading deletes, and on an interesting data model you don't want this -- and when it comes to views you'll just get an error and an aborted transaction. Prior to Django 1.5 you have to patch it to make DO_NOTHING actually mean "do nothing" -- otherwise it will still try to (needlessly) query and collect all related objects before going through its delete cycle, and the query will fail, halting the entire operation.
Incidentally, I wrote an in-depth explanation of how to do this just the other day.
You are trying to fetch records from a view. This is not correct as a view does not map to a model, a table maps to a model.
You should use Django ORM to fetch QryDescChar objects. Please note that Django ORM will fetch them directly from the table. You can consult Django docs for extra() and select_related() methods which will allow you to fetch related data (data you want to get from the other table) in different ways.