Display on a map objects matching a queryset with Django - json

I have a Django app, where each user can add a product with multiple possible metrics (width, height and length combination). A user must also specify in which city this product is located.
Users can also search within the database all products matching specific metrics.
I use Django 1.11 and am seaching for a solution to display on an interactive map all the products matching a queryset.
I am trying to do it with django-leaflet and django-geojson (as my db is not gis-oriented and I don't need heavy geo-computations), but I am facing some difficulties because my "PointField" is not in my product Model but in the Location Model and on the map I need to display Product properties, so I must serialize all these data together.
If you prefer code rather than words, here is a simplified version of my relevant files.
#models.py
class Product(models.Model):
name = models.CharField()
owner = models.ForeignKey(User)
photo = models.ImageField(...)
dimensions = models.ManyToManyField(Metrics)
location = models.ForeignKey(Location, related_name='products', related_query_name='product')
class Metrics(models.Model):
width = models.PositiveIntegerField()
height = models.PositiveIntegerField()
length = models.PositiveIntegerField()
class Location(models.Model):
zip_code = models.PositiveIntegerField()
city_name = models.CharField()
slug = models.SlugField(max_length=500, blank=True)
geom = PointField(default={'type': 'Point', 'coordinates': [0, 0]})
#views.py
class SearchResultListView(ListView):
model = models.Product
template_name='my_app/searchresult_list.html'
context_object_name = 'product_list'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
query_width = self.request.GET['width']
query_height = self.request.GET['height']
query_length = self.request.GET['length']
context['product_list'] = context['product_list'].filter(metrics__width=query_width,
metrics__length=query_length, metrics__height=query_height)
return context
#urls.py
????
#template.html
????
I saw in the django-geojson documentation multiple ways to hit the db (GeoJSON layer view,
Tiled GeoJSON layer view, GeoJSON template filter, low-level serialization). But I struggle to find the way to match my needs as my properties are in the Product Model, my coordinates are in the Location Model and my queryset in a non-related class-based view.
Any idea on the best way to perform my task? Should I continue with django-geojson or are there better apps for my purpose?

Your Product and Location look OK, but it is not clear what you are trying to do with Metrics. To select products near some place you want something like:
queryset = Product.objects.filter(location__geom__distance_lt=(someLocation, D(m=50)))
https://docs.djangoproject.com/en/3.0/ref/contrib/gis/geoquerysets/#distance-lt

Related

How to fetch latest entry in a related table while making sure the query is optimized

I have two models in consideration. RV_Offers and RV_Details. Each offer can have multiple details i.e. I have a foreignkey relationship field in RV_Details table.
Here is my view:
rv_offers_queryset = RV_Offers.objects.all().select_related('vendor').prefetch_related('details')
details_queryset = RV_Details.objects.all().select_related('rv_offer')
title = Subquery(details_queryset.filter(
rv_offer=OuterRef("id"),
).order_by("-created_at").values("original_title")[:1])
offers_queryset = rv_offers_queryset.annotate(
title=title).filter(django_query)
offers = RVOffersSerializer(offers_queryset, many=True).data
return Response({'result': offers}, status=HTTP_200_OK)
As can be seen, I am passing the offers queryset to the serializer.
Now, here is my serializer:
class RVOffersSerializer(serializers.ModelSerializer):
details = serializers.SerializerMethodField()
vendor = VendorSerializer()
def get_details(self, obj):
queryset = RV_Details.objects.all().select_related('rv_offer')
queryset = queryset.filter(rv_offer=obj.id).latest('created_at')
return RVDetailsSerializer(queryset).data
class Meta:
model = RV_Offers
fields = '__all__'
If you look at the get_details method, I am trying to fetch the latest detail that belongs to an offer. My problem is, even though I am using select_related to optimize the query, the results are still extremely slow, In fact, I am using django debug toolbar to inspect the query and apparently select_related seems to have no effect.
What am I doing wrong or how else can I approach this problem?
This is what I did to reduce the number of queries being hit on the db:
def get_details(self, obj):
details = obj.details.last()
return RVDetailsSerializer(details).data
I was able to reduce the number of queries from 45 to 4 using this.
This is because in the view, I had already used select_related to make the queryset, which in turn is being used here using obj.

How do I populate my form fields with data from the database Django

Hello I have a form page and I only need users to fill in certain fields, with the rest of the fields being pre-filled for them based on the module they pick.
While I can fetch the objects from my database -- i.e. the dropdown list shows Module Object (1), Module Object (2) -- I need only certain fields in these objects, which is why this similar sounding post couldn't answer my question:
Populate a django form with data from database in view
Here's my forms.py
class inputClassInformation(forms.Form):
module = forms.ModelChoiceField(queryset=Module.objects.all())
duration = forms.CharField(disabled=True, required=False)
description = forms.CharField()
assigned_professors = forms.ModelChoiceField(queryset=Class.objects.filter(id='assigned_professors'))
models.py -- not the full models are shown to reduce the post's length
class Module(models.Model):
subject = models.CharField(max_length=200, default="")
class Class(models.Model):
module = models.ForeignKey(Module, on_delete=models.CASCADE, default="")
duration = models.CharField(max_length=200, default="")
description = models.CharField(max_length=200, default="")
assigned_professors = models.CharField(max_length=200, default="")
So an expected result would be:
1) The Module field shows the subjects, instead of Module objects in its dropdown list and
2) The duration field is automatically filled in for the user, based on the module they picked. The reason is so that the user only has to manually fill in certain fields, while the rest are automatically generated.
This has had me stuck for a long while, help is appreciated. Thanks!
So an expected result would be:
1) The Module field shows the subjects, instead of Module objects in its dropdown list and
2) The duration field is automatically filled in for the user.
These are essentially two different questions.
To answer the first: you can override the:
__str__ method for your Model class for python 3 and django 1.8) and the
__unicode__ method for your Model class for django <1.8 and not python3.
For example, to make subjects appear instead of "XXXX Object" for your class do:
class Module(models.Model):
subject = models.CharField(max_length=200, default="")
def __unicode__(self):
return self.subject
Similarly, change __unicode__ for __str__ as appropriate for your django version.
Now, to answer your second question:
2) The duration field is automatically filled in for the user.
You need to do two things:
Do not display the duration field in your form (unless you want to give the users the chance to sometimes fill it in manually)
Override the save method
An example:
class Class(models.Model):
module = models.ForeignKey(Module, on_delete=models.CASCADE, default="")
duration = models.CharField(max_length=200, default="")
description = models.CharField(max_length=200, default="")
assigned_professors = models.CharField(max_length=200, default="")
def save(self, *args, **kwargs):
self.duration = #The value you'd want to automatically set here#
super(Model, self).save(*args, **kwargs)

Bug when trying to fill Many2many or One2many list in odoo v8

Since yesterday I'm facing a weird problem.I'm trying to add all the product category list to a contact at creation time.It's not working neither for a one2many/many2one relation or many2many/many2many relation. I always end up with an empty list of categories in the contact.
class product_category(models.Model):
_inherit = "product.category"
contacts = fields.Many2many('res.partner')
class odepoContact(models.Model):
_inherit = "res.partner"
categs = fields.Many2many('product.category')
#api.model
def create(self, values):
## Here categ is a list containing category ids.
categs = self.env['product.category'].search([])
# values['categs'] = (4,0,categs) Not working =>EMPTY
# values['categs'] = categs Not working =>EMPTY
# values['categs'] = (6,0,categs) Not working =>EMPTY
id = super(odepoContact, self).create(values)
_logger.error(id.categs)
return id
LOG:v8dev openerp.addons.org_chart_dept.org_chart: product.category()
Your create() should look like this:
#api.model
def create(self, values):
categs = self.env['product.category'].search([])
values['categs'] = [(6,0,categs.ids)]
id = super(odepoContact, self).create(values)
_logger.error(id.categs)
return id
That code example is for a Many2Many field (categs), which i would prefer here. For Many2Many you need to use a List of tuples. You can find the possible tuples here.

Can you include a TaggableManager as a filterset for django-filter?

I'm using both django-taggit and django-filter in my web application, which stores legal decisions. My main view (below) inherits from the stock django-filter FilterView and allows people to filter the decisions by both statutes and parts of statutes.
class DecisionListView(FilterView):
context_object_name = "decision_list"
filterset_class = DecisionFilter
queryset = Decision.objects.select_related().all()
def get_context_data(self, **kwargs):
# Call the base implementation to get a context
context = super(DecisionListView, self).get_context_data(**kwargs)
# Add in querysets for all the statutes
context['statutes'] = Statute.objects.select_related().all()
context['tags'] = Decision.tags.most_common().distinct()
return context
I also tag decisions by topic when they're added and I'd like people to be able to filter on that too. I currently have the following in models.py:
class Decision(models.Model):
citation = models.CharField(max_length = 100)
decision_making_body = models.ForeignKey(DecisionMakingBody)
statute = models.ForeignKey(Statute)
paragraph = models.ForeignKey(Paragraph)
...
tags = TaggableManager()
class DecisionFilter(django_filters.FilterSet):
class Meta:
model = Decision
fields = ['statute', 'paragraph']
I tried adding 'tags' to the fields list in DecisionFilter but that had no effect, presumably because a TaggableManager is a Manager rather than a field in the database. I haven't found anything in the docs for either app that covers this. Is it possible to filter on taggit tags?
You should be able to use 'tags__name' as the search/filter field. Check out the Filtering section on http://django-taggit.readthedocs.org/en/latest/api.html#filtering

Frequent update one filed of django model

Imagine, I have News models with many text fields
class News(models.Model):
title = models.CharField(max_length=255)
subtitle = models.CharField(max_length=255, blank=True)
lead = models.TextField(max_length=4096)
content = models.TextField()
...
last_visited = models.DateTimeField()
Every time my News object outputs, I update last_visited field:
news.last_visited = datetime.datetime.now()
news.save()
This code makes Django override all model fields:
UPDATE news SET title='...', subtitle='...', last_visited = '...' WHERE id = '...';
Instead of just one:
UPDATE news SET last_visited = '...' WHERE id = '...';
I worried how bad it is and is it worth of thinking about.
Django documentation offers queryset update but it looks not very elegant:
def upd(obj, **kwargs):
obj.__class__._default_manager.filter(pk=obj.pk).update(**kwargs)
upd(news, last_visited=datetime.datetime.now())
I use mysql backend.
Using update but with a cleaner approach:
class News(models.Model):
def update_visited(self):
News.objects.filter(pk=self.pk).update(
last_visited=datetime.datetime.now())
I think using queryset update is good. It removes the possibility that you overwrite changes to other fields by accident.
I know you're worried that it looks inelegant, but you only have to use it once in your upd function, then use upd in your views.
Supposing you want to use this on more than one model (guessing this because you pass obj to your upd function) it would probably make sense to have some base class that implements the last_visited field and your News class inherits from this class... Then you could do the update just on your base class.... Another possibilty would be putting the last_visited information into a seperate model and referencing the News model either through a ForeignKey or a GenericForeignKey (in the case you want to keep a 'history' for different models).