Hi everyone,
Let’s say I have 2 models - Shop and its Category:
# myapp/models.py
from django.db import models
class Category(models.Model):
name = models.CharField(max_length=30) # e.g., "sports", "fashion"
def __str__(self):
return self.name
class Meta:
verbose_name_plural = "categories"
class Shop(models.Model):
name = models.CharField(max_length=30) # e.g., "Joe's", "Rocky"
latitude = models.DecimalField(max_digits=8, decimal_places=6)
longitude = models.DecimalField(max_digits=9, decimal_places=6)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
def __str__(self):
return self.name
A shop is described by its name, latitude and longitude values, and its category (which is implemented as a foreign key). Here we assume that each shop has one and only one category.
The thing is that most online maps allow me to copy latitude and longitude values as a comma-separated string, like -33.852694685484394, 151.22593913730648 (copied from Google Maps). I think it would be nice to be able to input latitude and longitude in this format on the admin page. To achieve that, I created an inherited class:
# myapp/forms.py
import re
from django import forms
from django.core.exceptions import ValidationError
from .models import Shop
class ShopForm(forms.ModelForm):
# `lat_lon` is a char field for comma-separated pair of numbers
lat_lon = forms.CharField(label="Latitude, longitude")
class Meta:
model = Shop
fields = ["name", "lat_lon", "category"]
def __init__(self, *args, **kwargs):
super(ShopForm, self).__init__(*args, **kwargs)
# Fill `lat_lon` field with the existing data (if any)
if self.instance and self.instance.pk:
self.fields["lat_lon"].initial = \
f"{self.instance.latitude}, {self.instance.longitude}"
def _get_pair(self):
"""Return two floats from `lat_lon` field."""
return map(float, re.split(r",\s*", self.cleaned_data["lat_lon"]))
def clean(self):
# https://www.regular-expressions.info/floatingpoint.html
number_pattern = r"[-+]?[0-9]*\.?[0-9]+"
coords_pattern = number_pattern + r",\s*" + number_pattern
# Check if `lat_lon` field contains two comma-separated numbers
if not re.fullmatch(coords_pattern, self.cleaned_data["lat_lon"]):
raise ValidationError("Wrong coordinates")
# Check if these numbers are in the correct ranges
num_1, num_2 = self._get_pair()
if abs(num_1) > 90 or abs(num_2) > 180:
raise ValidationError("Wrong coordinates")
def save(self, commit=True):
obj = super(ShopForm, self).save(commit=False)
obj.latitude, obj.longitude = self._get_pair()
if commit:
obj.save()
return obj
Finally, I have the following code for admin:
# myapp/admin.py
from django.contrib import admin
from .forms import ShopForm, Shop
from .models import Category
class ShopAdmin(admin.ModelAdmin):
form = ShopForm
admin.site.register(Category)
admin.site.register(Shop, ShopAdmin)
All that works perfectly well. When I add a new shop or change an existing one, everything is fine:
However, I’m only a beginner in Django. Could you please tell me if I did all that in a correct way? Are there any important things here that I’m missing but I should be aware of?
Thank you.
