Cannot resolve added property of model in views - django

In my views.py file, I filter objects from a model, and I add some properties to the objects to use them later in templates.
matches = Match.objects.filter(season=season_name).order_by("match_date")
team_list = Team.objects.filter(id__in=team_ids)
for team in team_list:
home_matches = matches.filter(home_team=team)
away_matches = matches.filter(away_team=team)
team.sf = len(Shot.objects.filter(match__in=matches, team=team))
team.sa = len(Shot.objects.filter(match__in=matches, opponent_team=team))
team.pdo = ((team.f / team.sf) + (1 - (team.a / team.sa))) * 1000
team.xpdo = ((team.xgf / team.sf) + (1 - (team.xga / team.sa))) * 1000
For example, in my team model, fields sf, sa, pdo and xpdo does not exist. I calculate those fields on views and use them in a table in my template. There is no problem until here, actually. What I am trying to do is dumping query set as JSON and passing it the template to use in JavaScript.
context["teams_json"] = json.dumps(list(team_list.values_list(
"team_name", "pdo", "xpdo")), cls=DjangoJSONEncoder)
When I run the server, I get the following error:
FieldError at /leagues/turkey2017/
Cannot resolve keyword 'pdo' into field. Choices are: away_team, home_team, id, opponent_team, passmaplineup, passmappass, player_stats_opponent_team, player_stats_team, shooter_team, team_dark_color, team_light_color, team_name
I see that choices are combination of original fields in my Team model and ForeignKey fields in other models such as this field in Shot model:
opponent_team = models.ForeignKey(
Team, related_name="opponent_team", on_delete=models.CASCADE)
How can I use the fields added in views later on in views again?

The problem is that you are trying to use values_list on something that is not a field. I suppose that the reason you are doing so is to remove unnecessary fields from your serialized data. But really using values_list at all here is overkill; you just have three bits of data, you could just as easily dump them manually:
data = [{'pdo': team.pdo, 'xpdo': team.xpdo, 'team_name': team.team_name} for team in team_list]
context["teams_json"] = json.dumps(data)

Related

Django - Getting Related objects

There are such models:
class Nomenclature(models.Model):
nameNom = models.CharField(max_length=150,verbose_name = "Название номеклатуры")
numNom = models.CharField(max_length=50,verbose_name = "Номер номеклатуры",unique=True)
quantity = models.IntegerField(verbose_name="Количество", default=0)
numPolk = models.CharField(max_length=150,verbose_name = "Номер полки/места"
class Changes(models.Model):
numNomenclature = models.ForeignKey(Nomenclature, on_delete=models.CASCADE,related_name="chamges",verbose_name="Номер номеклатуры")
quantity = models.IntegerField(verbose_name="Количество",null=True)
location = models.CharField(max_length=50,verbose_name = "Место установки")
fullname = models.CharField(max_length=150,verbose_name = "ФИО")
appointment = models.CharField(max_length=50,verbose_name = "Назначение")
created_at = models.DateTimeField(auto_now_add=True,verbose_name='Дата/время', null=True)
It is necessary to output the name and number of the nomenclature and all related changes to the template, and also output all fields
I found that select_related exists, but I thought that it doesn't work the way I need it to.
I'm not completely sure if this is what you need.
If you need to fetch all of the changes, from a single "Nomenclature" model:
md = Nomenclature.objects.get(id=id) # Not sure how you fetch this, just an example.
all_changes_for_md = Changes.objects.filter(numNomenclature__id=md.id)
This will fetch you all changes for a nomenclature model.
Also possible to do it like this:
md = Nomenclature.objects.get(id=id) # Not sure how you fetch this, just an example.
all_changes_for_md = md.chamges.all() # You made a typo in the related name.
Select related has another purpose, it is used for prefetching.
From the Django docs:
select_related(*fields)
Returns a QuerySet that will “follow” foreign-key relationships, selecting additional related-object data when it executes its query. This is a performance booster which results in a single more complex query but means later use of foreign-key relationships won’t require database queries.
https://docs.djangoproject.com/en/4.1/ref/models/querysets/#select-related

How to filter multiple fields with list of objects

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))

Django ModelForm ChoiceField not displaying instance data

I have a ModelForm class in which I set a couple of the fields as ChoiceField. For one of my views, I'd like to create a form from my ModelForm class that pulls from an instance of my model in the database (like so):
form = MyModel(instance=model_instance)
When I do this and then render the form in a template, I've noticed that most of the fields are pre-populated with values pulled from the model instance, which is what I want. However, this isn't the case for two ChoiceField fields. These render as drop-down select menus with no specific option selected.
What's strange is if I don't define those two fields as ChoiceField-type in my ModelForm class, they render as normal text input fields in HTML and pre-populate using the database values. But when I define them so they show up as select-option input fields in HTML, nothing is pre-selected. Can I change this so that the values from the database are pre-selected?
EDIT: As requested here is the code for my model and form:
class App(models.Model):
CODES = (
(u'a',u'annual'),
(u'm',u'monthly'),
(u'w',u'weekly')
)
code = models.CharField(max_length=1, choices=CODES)
start_time = models.TimeField(blank=True, null=True)
end_time = models.TimeField(blank=True, null=True)
class AppForm(ModelForm):
CODES = (
(u'',u'Please select code'),
(u'a',u'annual'),
(u'm',u'monthly'),
(u'w',u'weekly')
)
TIMES = (
(u'00:00',u'All Day'),
(u'12:00',u'Noon')
)
start_time = forms.ChoiceField(required=False, choices=TIMES)
end_time = forms.ChoiceField(required=False, choices=TIMES)
code = forms.ChoiceField(choices=CODES, label='Type')
class Meta:
model = App
Interestingly, code field has the model instance value preselected just fine when rendered as HTML. I wonder if having the choices argument in the model definition makes the difference here?
UPDATE: I just noticed that if I pull up an App instance in the python manage.py shell like so:
a = App.objects.get(id=16)
a.start_time
I get a value like datetime.time(12, 0). But in the Django admin, when I'm looking at all of the App instances, all of them show (None) under start_time and end_time. Why would that be?
In response to your update : your times strings match default time string HH:MM format. Just like a user would enter them from website manually 12:00. The values get parsed and turned into time at model save (at validating really).
And when you load model - then of course the initial values loaded from object match the field's (models.TimeField) type.
If you replace your TIMES with
(datetime.time(0,0),u'All Day'),
(datetime.time(12,0),u'Noon')
your troubles should be over.
Alan

django save model ForeignKey relation

in my model.py
class Layer(models.Model):
user = models.IntegerField()
name = models
...
class Point(models.Model):
layers = models.ForeignKey(Layer)
meta = models.TextField()
...
in my view.py
def datasave(request, id):
mid = request.POST.get("layerid",default = "")
metas = request.POST.get("meta",default = "")
cs = Point()
cs.layers = mid
cs.meta = metas
cs.save()
but it gives an error in my django debug..in my project i use geodjango,openlayers and extjs... i didnt find any solution about saving my post
i didnt make any relation with my foreignkey.. basically i want to make a layer than when i want to add a point in my layer , i want save my point with layer id....
A more efficient way is to specify the foreign key by adding an "_id" at the end, like this:
cs = Point(layers_id = mid, meta = metas)
cs.save()
DO NOT do layers=XXX.objects.get(id=###) because that's doubling the number of database queries.
ALSO ... you're not cleaning your POST data here, which is pretty dangerous. Get it like this:
from django import forms
id_field = forms.IntegerField()
layer_id = id_field.clean(request.POST.get("layerId", "")
It's helpful to post the traceback to help understand your problem (for future questions).
It looks to me like you are passing in a number to your Point instance's layers attribute which would cause a ValueError. The only place you can pass a number to a foreign key field is in a django form which does the lookup for you.
You need to assign an actual Layer instance instead.
cs = Point()
cs.layers = Layer.objects.get(id=mid)
cs.meta = metas
cs.save()

Fetching ManyToMany objects from multiple objects through intermediate tables

Is there an easy way to fetch the ManyToMany objects from a query that returns more than one object? The way I am doing it now doesn't feel as sexy as I would like it to. Here is how I am doing it now in my view:
contacts = Contact.objects.all()
# Use Custom Manager Method to Fetch Each Contacts Phone Numbers
contacts = PhoneNumber.objects.inject(contacts)
My Models:
class PhoneNumber(models.Model):
number = models.CharField()
type = models.CharField()
# My Custom Manager
objects = PhoneNumberManager()
class Contact(models.Model):
name = models.CharField()
numbers = models.ManyToManyField(PhoneNumber, through='ContactPhoneNumbers')
class ContactPhoneNumbers(models.Model):
number = models.ForeignKey(PhoneNumber)
contact = models.ForeignKey(Contact)
ext = models.CharField()
My Custom Manager:
class PhoneNumberManager(models.Manager):
def inject(self, contacts):
contact_ids = ','.join([str(item.id) for item in contacts])
cursor = connection.cursor()
cursor.execute("""
SELECT l.contact_id, l.ext, p.number, p.type
FROM svcontact_contactphonenumbers l, svcontact_phonenumber p
WHERE p.id = l.number_id AND l.contact_id IN(%s)
""" % contact_ids)
result = {}
for row in cursor.fetchall():
id = str(row[0])
if not id in result:
result[id] = []
result[id].append({
'ext': row[1],
'number': row[2],
'type': row[3]
})
for contact in contacts:
id = str(contact.id)
if id in result:
contact.phonenumbers = result[id]
return contacts
There are a couple things you can do to find sexiness here :-)
Django does not have any OOTB way to inject the properties of the through table into your Contact instance. A M2M table with extra data is a SQL concept, so Django wouldn't try to fight the relations, nor guess what should happen in the event of namespace collision, etc... . In fact, I'd go so far as to say that you probably do not want to inject arbitrary model properties onto your Contact object... if you find yourself needing to do that, then it's probably a sign you should revise your model definition.
Instead, Django provides convenient ways to access the relation seamlessly, both in queries and for data retrieval, all the while preserving the integrity of the entities. In this case, you'll find that your Contact object offers a contactphonenumbers_set property that you can use to access the through data:
>>> c = Contact.objects.get(id=1)
>>> c.contactphonenumbers_set.all()
# Would produce a list of ContactPhoneNumbers objects for that contact
This means, in your case, to iterate of all contact phone numbers (for example) you would:
for contact in Contact.objects.all():
for phone in contact.contactphonenumbers_set.all():
print phone.number.number, phone.number.type, phone.ext
If you really, really, really want to do the injection for some reason, you'll see you can do that using the 3-line code sample immediately above: just change the print statements into assignment statements.
On a separate note, just for future reference, you could have written your inject function without SQL statements. In Django, the through table is itself a model, so you can query it directly:
def inject(self, contacts):
contact_phone_numbers = ContactPhoneNumbers.objects.\
filter(contact__in=contacts)
# And then do the result construction...
# - use contact_phone_number.number.phone to get the phone and ext
# - use contact_phone_number.contact to get the contact instance