Modifying save() in django model keeps looping - django

I'm trying to create a model based on the Google Map API.
If the object does not exists, I want to save the name, address, longitude, latitude and google place ID. Below is my code: However, when I run it, it goes into a loop and does stop checking Google Map. Can you tell me what is wrong?
class Place(models.Model):
name = models.CharField(max_length=200, null=True, blank=True)
address = models.CharField(max_length=200, null=True, blank=True)
logitude = models.CharField(max_length=20, null=True, blank=True)
latitude = models.CharField(max_length=20, null=True, blank=True)
id_google = models.CharField(max_length=50, null=True, blank=True)
date_created = models.DateTimeField(_('date created'), default=timezone.now)
date_modified = models.DateTimeField(_('date_modified'), auto_now=True)
def __str__(self):
return self.name
def save(self, *args, **kwargs):
try:
place = Place.objects.get(name=self.name)
except Place.DoesNotExist:
gmaps = googlemaps.Client(key=settings.GOOGLE_MAPS_API_KEY)
geocode_result = gmaps.geocode(self.address)
place = Place(name=self.name,
address=geocode_result[0]['formatted_address'],
logitude=geocode_result[0]['geometry']['location']['lng'],
latitude=geocode_result[0]['geometry']['location']['lat'],
id_google=geocode_result[0]['place_id'],
)
place.save()
return place

You call place.save() in the save(..) method, and thus triggering another save. You probably can just edit the item inplace, and then save it by using a super().save() call:
class Place(models.Model):
# ...
def save(self, *args, **kwargs):
try:
place = Place.objects.get(name=self.name)
except Place.DoesNotExist:
gmaps = googlemaps.Client(key=settings.GOOGLE_MAPS_API_KEY)
geocode_result = gmaps.geocode(self.address)[0]
self.address = geocode_result['formatted_address']
location = geocode_result['geometry']['location']
self.logitude = location['lng']
self.latitude = location['lat']
self.id_google = geocode_result['place_id']
super().save(*args, **kwargs)

Related

Django Admin, show inline based on slug

Have the following models
class FootballWebsite(models.Model):
"""Football service website."""
url = models.URLField, unique=True)
#football service
id = models.CharField(primary_key=True,
#is this domain blocked
blocked = models.BooleanField(default=False)
#is it online or offline
online = models.BooleanField(default=False)
updated = models.DateTimeField(auto_now=True, auto_now_add=True)
sub_categories = models.ForeignKey(SubCategory, default=1)
referral = models.TextField(blank=True)
mirror = models.ForeignKey('FootballWebsite', blank=True, null=True)
rank = models.PositiveIntegerField(default=0, blank=True, null=True)
screenshot = models.BooleanField(default=False)
class Meta:
"""Meta class."""
app_label = 'ahmia'
def __unicode__(self):
return u'%s' % (self.url)
"""The datetime when the football service was last seen online"""
try:
return self.footballwebsitestatus_set.filter(online=True).latest('time').time
except FootballWebsiteStatus.DoesNotExist:
return None
class FootballWebsiteDescription(models.Model):
"""Football service website description."""
about = models.ForeignKey(Footballebsite)
title = models.TextField(blank=True, null=True)
keywords = models.TextField(blank=True, null=True)
description = models.TextField(blank=True, null=True)
relation = models.URLField(blank=True, null=True)
subject = models.TextField(blank=True, null=True)
type = models.TextField(blank=True, null=True)
updated = models.DateTimeField(auto_now=True, auto_now_add=True)
language = models.TextField(null=True, blank=True)
contactInformation = models.TextField(null=True, blank=True)
officialInfo = models.BooleanField(default=False)
slug = AutoSlugField(populate_from=['title'], allow_duplicates=True, null=True)
class Meta:
"""Meta class."""
app_label = 'ahmia'
def __unicode__(self):
return unicode(self.about.url)
def save(self, *args, **kwargs):
self.slug = slugify(self.title)
super(FootballebsiteDescription, self).save(*args, **kwargs)
def __unicode__(self):
return u'%s' % (self.title)
I have a huge amount of links, and i would like to bulk assign them into a category or mark them as blocked based on identical title slug.
Managed to at least get a list of title_slugs with the code below, but the following step, i would like to get an inline list with all sites that have an identical title_slug and bulk assign those all in their a category
class FootballWebsiteInline(admin.TabularInline):
model = FootballWebsite
class FootballWebsiteDescriptionAdmin(admin.ModelAdmin):
list_display = ['show_slug']
def show_slug(self, obj):
return format_html("<a href='{url}'>{url}</a>", url=obj.slug)
inlines = [
FootballWebsiteInline,
]
Above code obviously doesn' t work, since the title slug which can appear many times is not a primary key.
Is it possible to get an inline list based on the title slug in this case which is not a primary key at all, or am i going about this the wrong way?
When possible at all, some further tweaking would be to group the identical title slugs

How to pass value into another model based on conditional statement

I want to create a countdown timer target that will be added into date_target model, using override save method in models.py, the time will countdown differently based on it's variety.
import datetime
class CustomerVariety(models.Model):
variety = models.CharField(max_length=100, null=True)
def __str__(self):
return self.variety
class Customer(models.Model):
cardnumber = models.CharField(max_length=15, null=False)
name = models.CharField(max_length=100, null=False)
date_in = models.DateTimeField(auto_now_add=False, auto_now=False, blank=True,
null=True)
date_target = models.DateTimeField(auto_now_add=False, null=True)
variety = models.ForeignKey(CustomerVariety,
on_delete=models.SET_NULL, null=True)
def __str__(self):
return self.name
def save(self, *args, **kwargs):
if not self.pk:
if self.variety == "Priority Customer":
self.date_target = self.date_in + datetime.timedelta(days=3)
elif self.variety == "Secondary Customer":
self.date_target = self.date_in + datetime.timedelta(days=5)
super(Customer, self).save(*args, **kwargs)
when i pass value into form and save it, why date_target models doesn't full fill the value ?

How to change slug if same string is stored in it?

I am working with slug on my model. Although, the entries for slug are not unique. When I try to go to a url containing slug, it says get() returned more than one object and I understand that it is because the entries are not unique. How am I supposed to change the slug a bit, if identical entries occur?
model
class Cabin(models.Model):
centre_name = models.ForeignKey(Centre, on_delete=models.CASCADE )
code = models.CharField(max_length=8, unique=True, default=unique_rand)
total_seats = models.IntegerField(blank='False')
category=models.CharField(max_length=100, default=False)
booked_date=models.DateField(blank='False')
released_date=models.DateField(blank='False')
price=models.IntegerField(blank=False, default=None)
slug = models.SlugField(unique=False,default=None,blank=True)
objects = UserManager()
def save(self, *args, **kwargs):
self.slug = slugify(self.category)
super(Client, self).save(*args, **kwargs)
First of all it might be better to set unique=True, such that this can never happen, furthermore you can :
class Cabin(models.Model):
centre_name = models.ForeignKey(Centre, on_delete=models.CASCADE )
code = models.CharField(max_length=8, unique=True, default=unique_rand)
total_seats = models.IntegerField(blank='False')
category=models.CharField(max_length=100, default=False)
booked_date=models.DateField(blank='False')
released_date=models.DateField(blank='False')
price=models.IntegerField(blank=False, default=None)
slug = models.SlugField(unique=True,default=None,blank=True)
objects = UserManager()
def save(self, *args, **kwargs):
slug = originalslug = slugify(self.category)
i = 0
while Cabin.objects.exist(slug=slug):
slug = '{}{}'.format(originalslug, i)
i += 1
self.slug = slug
super(Client, self).save(*args, **kwargs)
We here thus increment i until we found a slug that is not yet used.
Note that there exists an AutoSlugField in the django-extensions package [PyPi], that automates this slug procedure. For example:
from django.db import models
from django_extensions.db.fields import AutoSlugField
class Cabin(models.Model):
centre_name = models.ForeignKey(Centre, on_delete=models.CASCADE )
code = models.CharField(max_length=8, unique=True, default=unique_rand)
total_seats = models.IntegerField(blank='False')
category=models.CharField(max_length=100, default=False)
booked_date=models.DateField(blank='False')
released_date=models.DateField(blank='False')
price=models.IntegerField(blank=False, default=None)
slug = AutoSlugField(populate_from='category')
objects = UserManager()

Django Models custom methods

is there any way to call a specific model custom method from a view? i need to subtract or increment depending on the view a field on my model, I want to create a button for each of the two options and after imputing that data update the field in my database. if so how can i go about implementing it, currently my save method is doing the two operations at once
models.py
class Items(models.Model):
nombre = models.CharField(max_length=250)
descripcion = models.CharField(max_length=250)
codigo_proveedor = models.CharField(max_length=250)
categoria = models.ForeignKey('Categorias', on_delete=models.CASCADE)
c_minima = models.PositiveIntegerField()
c_actual = models.PositiveIntegerField()
c_descuento = models.PositiveIntegerField(blank=True)
c_incremento = models.PositiveIntegerField(blank=True)
proveedor = models.ForeignKey('Proveedores', on_delete=models.CASCADE)
carrito = models.ForeignKey('Carrito', on_delete=models.CASCADE, null=True )
p_unitario = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True )
total = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True)
material = models.ForeignKey(Materiales, null=True, blank=True)
tipo = models.ForeignKey(Tipo, null=True, blank=True)
active = models.BooleanField()
def save(self, *args, **kwargs):
self.c_actual = self.c_actual - self.c_descuento
self.c_actual =self.c_actual + self.c_incremento
self.total = self.c_actual * self.p_unitario
super(Items, self).save(*args, **kwargs)
def __str__(self):
return '%s %s %s %s' % (self.nombre, str(self.categoria), str(self.c_actual), str(self.total))
You could use an instance method on the model e.g. exactly like the get_absolute_url().
You can add 3 methods in the model definition increment, decrement and total and write the respective logic in there.
So the view in the views.py file be something like
def some_view(request, *args, **kwargs):
#read the post data values from req
#create model instance
data = Items( #key value pairs)
#read from req to increament or decrement
if incr:
data.increament()
else:
data.decrement()
data.total()
data.save()
return render( ... )

How to create an inline formset for a reverse foreign key relationship

I have a Property Model as follows =
class Property(models.Model):
property_type = models.CharField(max_length=255, default='Apartment')
specifications = models.CharField(max_length=255, default='Basic')
built_up_area = models.FloatField(max_length=6, null=False, default=0)
total_area = models.FloatField(null=False, default=0)
number_of_bedrooms = models.CharField(max_length=3, default=1)
number_of_bathrooms = models.CharField(max_length=3, default=1)
number_of_parking_spaces = models.CharField(max_length=2, default=0)
address_line_one = models.CharField(max_length=255, null=False)
address_line_two = models.CharField(max_length=255, null=True, default=None)
connectivity = models.CharField(max_length=255, default=None, null=True)
neighborhood_quality = models.CharField(max_length=255, default=None,
null=True)
comments = models.CharField(max_length=255, default=None, null=True)
city = models.ForeignKey('City')
state = models.ForeignKey('State')
pin_code = models.ForeignKey('PinCode')
developer = models.ForeignKey('Developer', null=True, default=None)
owner = models.ForeignKey('Owner', null=True, default=None)
created_by = models.ForeignKey('custom_user.User')
project = models.ForeignKey('Project')
def __unicode__(self):
return self.property_type
class Meta:
verbose_name_plural = 'Properties'
And a City model as follows -
class City(models.Model):
name = models.CharField(max_length=255)
slug = models.SlugField(unique=True)
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
super(City, self).save(*args, **kwargs)
def __unicode__(self):
return self.name
Now I want to make a single form where I can enter the Property details and while entering the city, I can enter the name of the city instead of selecting from the dropdown list.
So how do I create the inline formset using the inlineformset_factory to create the form?
==EDIT==
I've tried to use the following code to create the formset
CityFormset = inlineformset_factory(City, Property,
fields=('city',),
extra=0,
min_num=1,
can_delete=False)
You've misunderstood what an inline formset is. It's for editing the "many" side of a one-to-many relationship: that is, given a parent model of City, you could edit inline the various Properties that belong to that city.
You don't want a formset at all to simply edit the single City that a property can belong to. Instead, override the city field within your Property form to be a TextField, and either create a new City or find an existing one in the clean_city method.
class PropertyForm(forms.ModelForm):
city = forms.TextField(required=True)
class Meta:
model = Property
exclude = ('city',)
def __init__(self, *args, **kwargs):
super(PropertyForm, self).__init__(*args, **kwargs)
if self.instance and not self.data:
self.initial['city'] = self.instance.city.name
def save(self, commit=True):
city_name = self.cleaned_data['city']
city, _ = City.objects.get_or_create(name=city_name)
instance = self.save(commit=False)
instance.city = city
if commit = True:
instance.save()
return instance