I get the relative objects from queryset, but it have a further relative query, how to get those objects?
class PropertyTaxItem(models.Model):
property = models.ForeignKey(Property)
class Property(models.Model):
citizens = models.ManyToManyField(Citizen, null=True, through = 'Ownership',help_text="a property could belong to multiple citizens")
class Citizen(models.Model):
first_name = models.CharField(max_length = 50, help_text = 'First name')
last_name = models.CharField(max_length = 50, help_text = 'Last name')
my getting the citzien part:
items = PropertyTaxItem.objects.filter(i_status='active').select_related('property_id').prefetch_related('property_id.citizens')
for i in items:
pp.pprint(i.citizens.__dict__)
the output is:
{'_db': None,
'_fk_val': 10092,
'_inherited': False,
'core_filters': {'property__pk': 10092},
'creation_counter': 69,
'instance': <Property: 306 GASHIHA, KAGINA, Kicukiro,Kicukiro>,
'model': <class 'citizen.models.Citizen'>,
'prefetch_cache_name': 'citizens',
'query_field_name': 'property',
'reverse': False,
'source_field_name': 'asset_property',
'symmetrical': False,
'target_field_name': 'owner_citizen',
'through': <class 'property.models.Ownership'>}
but I want get the items of citizens like:
{'id': 18980,
'first_name': 'Jack',
'last_name' : 'blablabla',
....
}
how to do it?
You have a few issues. You are querying PropertyTaxItem which foreign keys to Property, which then has multiple Citizens. This would be a correct query to select the related 'propertys and prefetch theproperty.citizens`
items = PropertyTaxItem.objects.filter(i_status='active').select_related('property').prefetch_related('property__citizens')
You can access the citizens like this:
for i in items:
citizens = i.property.citizens.all() # now you have a queryset of citizens.
# To get a list of dictionaries, you can call `.values()` on the queryset.
pp.pprint(citizens.values())
Another possible problem you will have is that citizens uses a custom through model. There are some limitations on ManyToManyField query sets when using custom though models.
Related
Hi guys so I have a search function but when using the .objects.filter() method I get a queryset that shows an ID instead of the username.
This is the view:
def search_expense(request):
if request.method == 'POST':
search_str = json.loads(request.body).get('searchText')
expenses = Expense.objects.filter(
amount__istartswith=search_str) | Expense.objects.filter(
date__icontains=search_str) | Expense.objects.filter(
description__icontains=search_str) | Expense.objects.filter(
category__icontains=search_str)
data = expenses.values()
return JsonResponse(list(data), safe=False)
<QuerySet [{'id': 16, 'amount': 2.33, 'date': datetime.date(2020, 10, 2), 'description': 'Something', 'owner_id': 1, 'category': 'Food'}]>
So instead of the 'owner_id': 1 I need it to be 'owner': username
The model (the User model is Django's standard model):
class Expense(models.Model):
amount = models.FloatField()
date = models.DateField(default=now)
description = models.TextField()
owner = models.ForeignKey(to=User, on_delete=models.CASCADE)
category = models.CharField(max_length=255)
def __str__(self):
return self.category
class Meta:
ordering: ['-date']
You can add new fields to .values().
from django.db.models import F
fields = tuple(x.name for x in Expense._meta.get_fields())
data = expenses.values(*fields, owner_name=F("owner__value"))
You will also have to specify all the fields you want manually, or if you want all the fields then you can just use .get_fields() like I have done.
You have to name the new field something other than "owner", or you will get a ValueError:
ValueError: The annotation 'owner' conflicts with a field on the model.
Showing the ID of ForeignKey fields is the intended behaviour of .values() according to the documentation:
If you have a field called foo that is a ForeignKey, the default values() call will return a dictionary key called foo_id, since this is the name of the hidden model attribute that stores the actual value (the foo attribute refers to the related model).
I want to get x and y coordinates from the point in the following queryset:
user = User.objects.values('id', 'email', 'username', 'point').first()
These are my models:
from django.db.models import Model
from django.contrib.gis.db.models import PointField
class BasePointModel(Model):
point = PointField(null=True, spatial_index=True, geography=True)
class Meta:
abstract = True
class User(AbstractUser, BasePointModel, HashableModel): # type: ignore
# First Name and Last Name do not cover name patterns
# around the globe.
name = CharField(_("Name of User"), blank=True, max_length=255)
is_online = BooleanField(_("Is online"), default=False)
I am getting the following result:
{'id': 85270,
'email': 'username_0#email.com',
'username': 'username_0',
'point': <Point object at 0x7f8e061cc2b8>}
How can I get an x and y, making a queryset with values?
I want to get something like this:
{'id': 85270,
'email': 'username_0#email.com',
'username': 'username_0',
'point__x': '-4.266398314110177',
'point__y': '-39.39432682357033'}
I make requests that return 50,000 lines from a database, and I notice that performance is getting worse due to data serialization. I want to minimize serialization by returning values.
But the PointField always returns an object, and I still haven't found a way to serialize it in the best way.
Tough luck :/
You are setting the points as geography and therefore you cannot use a custom GeoFunc in order to apply PostGIS's ST_X and ST_Y in an annotation query because they will only work on geometry fields.
In order to get the [lat, long] values of those points in the values results of the query we can annotate the query with the GeoJSON representation of the points and get those as values. To do that we will use the AsGeoJSON database function:
user = User.objects.annotate(
point_as_json=AsGeoJSON('point')
).values('id', 'email', 'username', 'point_as_json').first()
which will give us the following result:
{
'id': 85270,
'email': 'username_0#email.com',
'username': 'username_0',
'point_as_json': {
'type': 'Point',
'coordinates': [-4.26639831, -39.39432682]
}
}
As a side note, you can consider augmenting Django with django-rest-framework` and use django-rest-framework-gis on top of that which has some very handy tricks on serialization according to GeoJSON standards.
You could access the __dict__ method of your object and then directly with your geometry:
user = User.objects.values('id', 'email', 'username', 'point').first()
user_dict = user.__dict__
user_dict.update({'point__x':user_dict['point'].x, {'point__y':user_dict['point'].y})
The best way to get what you need is through a Serializer like the one bellow.
class LayerGeometrySerializer(GeoFeatureModelSerializer):
"""
GeoFeatureModelSerializer is a subclass of rest_framework.ModelSerializer which will output data in a format that is GeoJSON compatible
"""
values = LayerValueMinimalSerializer(source='layervalue_set', many=True)
class Meta:
model = LayerGeometry
geo_field = 'geom'
exclude = ('created', 'modified', )
I have these serializers in my app:
class ScheduleSerializer(serializers.ModelSerializer):
class Meta:
model = Schedule
fields = ('id',)
class DisciplineSerializer(serializers.ModelSerializer):
class Meta:
model = Discipline
fields = ('id',)
class WriteTeacherSerializer(serializers.ModelSerializer):
disciplines = DisciplineSerializer(many=True)
schedules = ScheduleSerializer(many=True)
class Meta:
model = Teacher
fields = ('phone_number', 'bio', 'price', 'disciplines', 'schedules')
depth = 1
def update(self, instance, validated_data):
print "\n"
#Debugging here
print validated_data
print "\n"
print instance.__dict__
print "\n"
instance.phone_number = validated_data['phone_number']
instance.bio = validated_data['bio']
instance.price = validated_data['price']
disciplines = validated_data.pop('disciplines')
schedules = validated_data.pop('schedules')
for discipline in disciplines:
try:
stored_discipline = Discipline.objects.get(id=discipline['id'])
instance.disciplines.add(stored_discipline)
except Discipline.DoesNotExist:
raise Http404
for schedule in schedules:
try:
stored_schedule = Schedule.objects.get(id=schedule['id'])
instance.schedules.add(stored_discipline)
except Discipline.DoesNotExist:
raise Http404
instance.save()
return instance
As you can see I am trying a nested serialization with the fields schedules and disciplines. I think I followed the documentation, but the nested serialization is not working when I test it. I printed the instance and validated_data objects and tested it on the shell.
I start the data in this format:
data = {u'phone_number': u'+99999999999', u'bio': u'BIO', u'price': 40, u'disciplines': [{'id': 1}], u'schedules': [{'id': 2}]}
I got a teacher instance and started the serializer like this:
serializer = WriteTeacherSerializer(teacher, data=data)
It shows True on a serializer.is_valid() call.
However when I try to save it the validated_data and the instance.__dict__ are like that:
#validated_data
{u'phone_number': u'+5584998727770', u'bio': u'BIO', u'price': 40, u'disciplines': [OrderedDict()], u'schedules': [OrderedDict()]}
#instance.__dict__
{'phone_number': u'', 'bio': u'', 'price': 50, 'profile_id': 2, '_state': <django.db.models.base.ModelState object at 0xb64a6bec>, 'id': 6}
They don't seem to notice the nested fields wich makes the update() method not work.
Am I doing something wrong?
Here is my Teacher Model as well:
class Teacher(models.Model):
price = models.IntegerField(default=50)
phone_regex = RegexValidator(regex=r'^\+?1?\d{9,15}$', message="Wrong phone number format.")
phone_number = models.CharField(validators=[phone_regex], max_length=15, blank=True)
profile = models.OneToOneField(Profile, on_delete=models.CASCADE)
schedules = models.ManyToManyField(Schedule, related_name='schedules')
disciplines = models.ManyToManyField(Discipline, related_name='disciplines')
bio = models.CharField(max_length=200, blank=True)
If you are just sending IDs then you don't need to add the nested serializer, just specify the field name of the ForeignKey or ManyToManyField.
class WriteTeacherSerializer(serializers.ModelSerializer):
class Meta:
model = Teacher
fields = ('phone_number', 'bio', 'price', 'disciplines', 'schedules')
I am also wondering if it is because you have a depth=1 flag?
DRF doesn't support nested updates out of the box. You have to override the Serializer's update method, and write your own update logic, so you'd be seeing an error for this warning if you were sending nested data.
I'm new in django and a simple query blocks me..
So, here is a slice of my model:
class History(models.Model):
timestamp = models.DateTimeField(auto_now_add=True)
name = models.CharField(max_length=200)
user = models.ForeignKey(User)
state = models.ForeignKey(Status)
reason = models.TextField()
gpgkey = models.ForeignKey(Key)
def __unicode__(self):
return self.name
class Key(models.Model):
gpgkey = models.CharField(max_length=128)
gpgid = models.CharField(max_length=32)
path = models.CharField(max_length=200)
def __unicode__(self):
return self.gpgkey
this query
History.objects.filter(pk=1).values()
returns:
[{'user_id': 1, 'name': u'test-4.30-6', 'timestamp': datetime.datetime(2015, 1, 1, 20, 2, 0, 578794, tzinfo=<UTC>), 'gpgkey_id': 1, 'reason': u'blablabla', 'state_id': 2, u'id': 1}]
Target is, that for ex. gpgkey_id is gpgkey : { gpgkey : "test", gpgid : "06B7FFF" } and so on for all the other related objects.
How can i do that?
I use values(), because I want the Fields and not the whole model.
you have to ask for them in the values method parameters, and unfortunately I think you must specify also the default you want for you starting model:
History.objects.filter(pk=1).values('gpgkey__gpgkey', 'gpgkey__gpgid', 'gpgkey__path', 'timestamp', 'name', 'user', 'state', 'reason')
if you have a long list of attributes something better can be made traversing the _meta.fields attribute of your model and building that parameter list from there, but the base idea is the previous one.
I am getting this type result from query object:
report_qs = CustomReport.objects.all().filter(id=report_id)
[<CustomReport: {'Name': 'Zara', 'Age': 7, 'Class': 'First'}>]
CustomReport Model:
class CustomReport(models.Model):
"""
Save the result of the custom report form wizard for further
regeneration. The result is saved as a serialized dictionary.
"""
class Meta:
unique_together = (('creation_date', 'name'),)
name = models.CharField(max_length=64)
query = models.TextField()
creation_date = models.DateTimeField(default=datetime.datetime.now,
editable=False)
def __unicode__(self):
return self.query
How to get values and keys?
You Should try
report_qs = CustomReport.objects.all().filter(id=report_id).values('name', 'age', 'class')
Now see report_qs you can easily get values and keys.
You can iterate report_qs
for obj in report_qs:
print obj.keys()
print obj.values()