Best practices for GeoDjango Models - django

I have a classic GeoDjango model
from django.contrib.gis.db import models
class Location(models.Model):
vessel = models.ForeignKey(Vessel, on_delete=models.CASCADE)
timestamp = models.DateTimeField(auto_now_add=True, blank=True)
point = models.PointField()
However usage seems quite clumsy;
>>> foo = somevessel.location_set.create(point=Point(5, 23))
>>> foo.point.x
5.0
I still want to store the location as a point but would prefer to interact with the model with more native looking code, something like
>>> foo = somevessel.location_set.create(latitude=5, longitude=12)
>>> foo.latitude
5.0
Is this against best practices? And is there a simple way to achieve this in Django?

I think that implicitly formalizing your point like this, i.e. with latitude and longitude FloatFields, will not allow you to fully benefit from spatial lookup capabilities. If not a "bad practice", this is likely to be a no-starter.
I still want to store the location as a point but would prefer to interact with the model with more native looking code,
To interact with the model in a more native looking way, I would define properties in my model class which would actually return latitude and/or longitude, doing
from django.contrib.gis.db import models
class Location(models.Model):
vessel = models.ForeignKey(Vessel, on_delete=models.CASCADE)
timestamp = models.DateTimeField(auto_now_add=True, blank=True)
point = models.PointField()
#property
def latitude(self):
return self.point.x
#property
def longitude(self):
return self.point.y
Allowing you to do
>>> foo = somevessel.location_set.create(point=Point(5, 23))
>>> foo.latitude
5.0

Related

Django: Instanciation of a model using a hash of a ForeignKey

I'm currently trying to implement an API in Django Rest for a class project that's supposed to store data in a secure way.
I have two different models:
class Driver(models.Model):
name = models.CharField(max_length=100, blank=False, null=False)
and
import Driver
class Car(models.Model):
name = models.CharField(max_length=100, blank=False, null=False)
driver = models.ForeignKey(Driver, on_delete=models.CASCADE)
Currently, there's a mapping between cars and their respective drivers.
I'd like to map a car not to it's driver, but to the result of a hash function applied to it's driver name. Something like:
import Driver
import hashlib
class Car(models.Model):
name = models.CharField(max_length=100, blank=False, null=False)
driver = hashlib.md5(models.ForeignKey(Driver, on_delete=models.CASCADE).name).hexdigest()
I know what I wrote above is probably heretical in Django, it is just to convey the idea of what I want to do!
I thought about overriding the Car model's init, save, create functions in order to do this, but have strictly no idea what is the good option.
At the end of the process I'd like that there's no mapping between the Car and it's Driver, but a Driver's car could be found using the hash of his name.
Let me know if this is unclear or if you need more precision!
Any help would be appreciated,
Thanks!
You want to remove the explicit link from Car to Driver so you should not use a ForeignKey. Note the disadvantage is that you can't use a database JOIN to retrieve the car for a driver but will have to perform an extra query.
Create methods on your Car and Driver models to assign a driver and to retrieve the car:
class Car(models.Model):
...
driver = models.CharField(max_length=255)
def assign_driver(self, driver):
self.driver = hashlib.md5(driver.name).hexdigest()
class Driver(models.Model):
#property
def car(self):
try:
return Car.objects.get(driver=hashlib.md5(self.name).hexdigest())
except Car.DoesNotExist:
return None
This way, you can do my_driver.car to fetch the car for this driver of some_car.assign_driver(my_driver); some_car.save() to assign and save a driver.

How to save old IDs / reference numbers from several models?

what is the best conceptional way to save old IDs / reference numbers from several models?
For example:
class Project(models.Model):
reference_number = models.CharField(_('Project ID'), max_length=16,
unique=True)
class Offer(models.Model):
reference_number = models.CharField(_('Offer ID'), max_length=16,
unique=True)
My ideas:
A separated model for each of the models
class OldProjectNumber(models.Model):
project = models.ForeignKey(Project)
old_number = models.CharField(_('Old ID'), max_length=16,
unique=True)
...
One model for all the models?
class OldNumber(models.Model):
project = models.ForeignKey(Project)
offer = models.ForeignKey(Offer)
old_number = models.CharField(_('Old ID'), max_length=16,
unique=True)
Something else?
Maybe a CommaSeparatedCharField to keep the values in the model itself?
I need to be able to search / filter for the old reference numbers for each model.
Any suggestions are appreciated.
It depends on how many old reference will be there.
If there will be only a few old reference numbers you can use CommaSeparatedCharField but in this you will have to specify the max_length which will limit it to few reference numbers and cause problem anytime in future.
I would suggest you to use a textfield and use a json formatted string to store the list of old references.
If the count of reference number can get large, you should use One models for all the models to store the reference number.
Also, Instead of using a foreign key for each of one of models you should use generic foreign key which will map to all other models.
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
from django.contrib.contenttypes.models import ContentType
class OldNumber(models.Model):
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
and Generic Relation on the model itself.
old_reference = GenericRelation('OldNumber', related_query_name='model_name') # related_query_name for reverse querying

Storing restaurant categories in GeoDjango project

I am making a geo django backend for an iPhone app. The iphone app sends users current location to the backend, which returns nearby restaurants (something similar to Foursquare and Yelp.)
I am not entirely sure how to store the cuisine for the restaurants. I need an option where the user can look up/select a specific cuisine for example only Chinese food. Should I create a seperate model for restaurant cuisine and have one to many relationship with the restaurants ? or should I use boolean value like this:
models.BooleanField(default=False)
Here is the code for my Model:
from django.db import models
from django.contrib.gis.db import models as gis_models
from django.contrib.gis import geos
from django.db import models
# Create your models here.
class Restaurant(models.Model):
name = models.CharField(max_length = 100)
address = models.CharField(max_length = 150)
phone = models.CharField(max_length = 12)
cuisine = models.CharField(max_length = 50)
eatingOptions = models.CharField(max_length = 50)
location = gis_models.PointField(u'Latitude/Longitude', geography=True, blank=True, null=True)
# Query Manager
gis = gis_models.GeoManager()
objects = models.Manager()
def __unicode__(self):
return self.name
Only you and your requirements can answer that.
If you create a separate model and have a relationship it will be more flexible, you can have different cuisines without changing the model. Best option if your database will have multiple types of cuisine.
If you choose to use a BooleanField like is_chinese then it will work only for chinese cuisine. This is the best option if you only care for one type of cuisine.
Don't worry if you make a bad judgement when developing (at least on an early stage), django has your back.
Hope that helps.

Django reverse ForeignKey lookup returns None

I'm new to Django development and have just started writing an app.
I have two classes defined in models.py:
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
class NEO(models.Model):
name = models.CharField(max_length=100, default=' ')
sighter = models.ForeignKey(User, blank=True, null=True)
date_sighted = models.DateTimeField(default=timezone.now())
ratings = models.IntegerField(default=0)
#coords = models.ForeignKey('Coords', default='')
def __unicode__(self):
return self.name
class Coords(models.Model):
ra = models.FloatField('Right Ascension', default=0)
dec = models.FloatField('Declination', default=0)
neo = models.ForeignKey(NEO, related_name='neo_coords', null=True)
def __unicode__(self):
return str(self.ra) + ' ' + str(self.dec)
Each Coords object links to a single NEO and vice versa.
Uncommenting the Neo.Coords line and then calling n.Coords returns a None. Given a NEO object, how can I get the corresponding Coords object?
ForeignKey here is a ManyToOne relationship (as suggested in the docs), So in your case multiple Coords objects can be binded to a single NEO object. If you want a OneToOne Relation you may want to use models.OneToOneField (documentation here).
In case of foreign key's lookup you can use.
NEO.coords_set.get(**lookup_arguments_here)
# Here NEO.coords_set is the list of coords objects bound to this particular NEO object.
and in case of OneToOne you can simply use
NEO.coords
It doesn't make sense to have two tables referencing each other with dual foreign keys because you run into a chicken or the egg problem. You need to decide whether or not there can be a one-to-many relation or a one-to-one relation.
Can a NEO have multiple Coords? Can a Coord have multiple NEOs? If the answer is yes, then you need a ForeignKey. The ForeignKey should be on the many of the one-to-many side of the relation. If the answer was no, and there can only be a one-to-one link, then you want a OneToOneField.
To access the reverse side of the relationship it is simple:
# multiple coords per neo
class NEO(models.Model):
name = ...
class Coords(models.Model):
name = ...
neo = models.ForeignKey(NEO)
c = Coords.objects.get(id=1)
c.neo # shows the neo
n = NEO.objects.get(id=1)
coords = n.coords_set.all() # multiple coords per neo
If instead you had a one to one relationship:
class NEO(models.Model):
name = ...
class Coords(models.Model):
name = ...
neo = models.OneToOneField(NEO)
c = Coords.objects.get(id=1)
c.neo # shows the neo
n = NEO.objects.get(id=1)
coords = n.coords # only one possible coord per neo
https://docs.djangoproject.com/en/dev/topics/db/queries/#lookups-that-span-relationships

Django: Best practice with Foreignkeys and multiple models

Hi Stackoverflow people,
What is the best way to refer from one model to a choice of ForeignKeys?
I am working on a rental app, which includes a model for the GenericVehicle, Bikes, and Cars.
class GenericVehicle(models.Model):
licence = models.CharField(max_length=128)
...
class Meta:
abstract = True
class Bike(GenericVehicle):
engine_type = models.CharField(max_length=128)
...
class Car(GenericVehicle):
number_of_doors = models.SmallIntegerField()
...
Now I have a model for the rental registration, where I would like to register the rented vehicle. I am unsure about the best practise here. So far, I had two foreignkeys and made sure that at least one is filled. But this solution seems very inefficient and does not scale well for multiple vehicle types.
What is the best way to improve the class structure/definition?
class Rental(models.Model):
rental_bike = models.ForeignKey(Bike)
rental_car = models.ForeignKey(Car)
rental_date = ...
Thank you for your advice. I am trying to find an efficient solution for some time already.
Django offers you a GenericForeignKey. A GenericForeignKey will need to fields on you model, one for saving the referenced model's ContentType and a second one to save the object's id:
from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic
class Rental(models.Model):
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
rental_vehicle = generic.GenericForeignKey('content_type', 'object_id')
But keep in mind that this not a foreign key on database level, just Django kind of emulating some of the typical behaviour of a foreign key.