Django Models custom methods - django

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( ... )

Related

Autofilling Django model form field with data from associated objects

I have a model form that creates a new job entry, and on submission, I need an invisible field job_time_estimation to be set to a sum of 'service_stats_estimate_duration' values from ServiceItemStats objects associated with the JobEntry by a many-to-many relationship when submitting the form.
For example, if in my NewJobEntryForm I chose two existing ServiceItemStats objects that have service_stats_estimate_duration values 60 and 90, on submission, I want a value 150 to be saved in that JobEntry object's job_time_estimation attribute.
I tried doing this using aggregation by defining a save() method in the model but I am getting an error "name 'serviceItemStats' is not defined".
I am not sure if I am going about this the right way. Any help would be appreciated.
My code:
models.py:
class ServiceItemStats(models.Model):
service_stats_name = models.CharField(primary_key=True, max_length=20)
service_stats_estimate_duration = models.IntegerField()
# Many-to-many relationship with JobEntry.
def __str__(self):
return self.service_stats_name
class JobEntry(models.Model):
# PK: id - automatically assigned by Django.
jo
b_entry_date_time = models.DateTimeField(default=timezone.now)
jo
b_date = models.DateField(blank=True, null=True)
job_checked_in = models.BooleanField()
job_checked_out = models.BooleanField(default=False)
job_priority = models.IntegerField()
job_time_estimation = models.IntegerField(blank=True, null=True)
job_comments = models.TextField(max_length=200, blank=True, null=True)
job_parts_instock = models.BooleanField(default=False)
job_started = models.BooleanField(default=False)
job_finished = models.BooleanField(default=False)
job_expand_fault_evidence = models.ImageField(blank=True, null=True)
job_expand_comments = models.ImageField(blank=True, null=True)
job_expand_parts_required = models.CharField(max_length=200, blank=True, null=True)
vehicle = models.ForeignKey(Vehicle, on_delete=models.CASCADE) #One-to-one relationship
customer = models.ForeignKey(Customer, on_delete=models.CASCADE) #One-to-one relationship
serviceBay = models.ForeignKey(ServiceBay, on_delete=models.CASCADE, blank=True, null=True) #One-to-one relationship
serviceItemStats = models.ManyToManyField(ServiceItemStats, blank=True) #Many-to-many relationship
def __str__(self):
return self.id
def save(self, *args, **kwargs):
if not self.job_time_estimation:
self.job_time_estimation = serviceItemStats.objects.all().aggregate('service_stats_estimate_duration')
return super().save(*args, **kwargs)
def get_absolute_url(self):
return reverse("jobs:job_detail",kwargs={'pk':self.pk})
views.py
class JobCreateView(FormView):
template_name = "jobs/jobentry_form.html"
form_class = NewJobEntryForm
success_url = reverse_lazy("jobs:job_list")
def form_valid(self, form):
form.save()
return super(job_list, self).form_valid(form)
forms.py
class NewJobEntryForm(ModelForm):
class Meta:
model = JobEntry
fields = ['vehicle', 'customer', 'job_date', 'job_checked_in', 'job_priority', 'job_comments', 'job_parts_instock', 'serviceItemStats']
widgets = {
'job_date' : forms.DateInput(format=('%m/%d/%Y'), attrs={'class':'form-control', 'placeholder':'Select a date', 'type':'date'}),
'ServiceItemStats' : forms.CheckboxSelectMultiple(),
'job_priority' : forms.RadioSelect(choices=priorityOptions),
}
You can try this.
from django.db.models import Sum
class JobCreateView(FormView):
template_name = "jobs/jobentry_form.html"
form_class = NewJobEntryForm
success_url = reverse_lazy("jobs:job_list")
def form_valid(self, form):
job=form.save()
estimation = job.serviceItemStats.all().aggregate(total=Sum('service_stats_estimate_duration'))
job.job_time_estimation = estimation['total']
job.save()
return super(job_list, self).form_valid(form)

Modifying save() in django model keeps looping

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)

How can i get a object from a other function in django

I have two functions projectTimerStart to start the timer and projectTimerStop
i want to use the object which is created in projectTimerStart and i want to end the time when projectTimerStop , and this should be saved in a database
ps: Both the functions are not in class they are normal functions
def projectTimerStart(request, slug):
project_detail = Project.objects.get(slug=slug)
b = ProjectTimer(time_started=datetime.now(),
working_project=project_detail,
working_freelancer=request.user
)
b.save()
return HttpResponseRedirect(reverse('project_timer', kwargs=
{"slug":slug}))
def projectTimerStop(request, slug):
project_detail = Project.objects.get(slug=slug)
#i want something here super method or something
return HttpResponseRedirect(reverse('project_timer', kwargs=
{"slug": slug}))
models.py
class Project(models.Model):
project_title = models.CharField(max_length=100)
project_description = models.TextField()
created_by = models.ForeignKey(
User, on_delete=models.CASCADE, related_name='created')
assigned_to = models.ManyToManyField(
User, blank=True, related_name='assigned_by')
slug = models.SlugField()
hourly_budget = models.PositiveIntegerField(blank=True, null=True)
technologies = models.ManyToManyField(
Technologies, related_name='technologies_used')
time_posted = models.DateTimeField(auto_now_add=True)
request_id = models.ManyToManyField(
User, related_name='requested_by', blank=True)
def __str__(self):
return self.project_title
def save(self, *args, **kwargs):
self.slug = slugify(self.project_title)
super(Project, self).save(*args, **kwargs)
def get_absolute_url(self):
return reverse('project_detail', kwargs={'slug': self.slug})
def get_timer_url(self):
return reverse('project_timer', kwargs={'slug': self.slug})
def round_datetime(dt):
minutes = round((dt.minute + float(dt.second) / 60) / 15) * 15 -
dt.minute
return dt + datetime.timedelta(minutes=minutes, seconds=-dt.second)
class ProjectTimer(models.Model):
time_started = models.DateTimeField()
time_ended = models.DateTimeField(blank=True, null=True)
working_project = models.ForeignKey(Project, on_delete=models.CASCADE)
working_freelancer = models.ForeignKey(
User, on_delete=models.CASCADE, blank=True, null=True)
If each of your project objects will have one and only project timer objects, you can add project_timer = models.OneToOneField(ProjectTimer) to your Project model and access to the project timer by using project_detail.project_timer.
If not, you need to know at least one feature of that project_timer in order to fetch it from database. Or you can iterate all of your ProjectTimer objects that belongs to that Project and select the appropriate one by:
models.py
class Project(models.Model):
# Some fields
project_timers = models.ManyToManyField(ProjectTimer)
views.py
def projectTimerStop(request, slug):
project_detail = Project.objects.get(slug=slug)
for pt in project_detail.project_timers.all():
if pt.some_field == "THIS IS CORRECT TIMER":
# Here is your project_detail
print(str(pt))
return HttpResponseRedirect(reverse('project_timer', kwargs=
{"slug": slug}))

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

Serialize a related models in Django

I am trying to serialize software model with its related image fields. Images are refered to Software model with foreign key and when i serialize the software model i want it to return the images or image paths as well. I cannot make a image field on Software model since multiple images will be relating to it. How would i serialize a model and its related models that are refered by a foreign key?
class Software(models.Model):
"""
Base model for listings
"""
SOFTWARE_TYPE = (
('R', 'Retail'),
('H', 'Hospitality'),
)
objects = SoftwareManager()
slug = models.SlugField(unique=True,blank=True, null=True)
submitted_by = models.ManyToManyField(User, blank=True, null=True)
title = models.CharField(max_length=120, blank=True, null=True)
developer = models.CharField(max_length=120, blank=True, null=True)
address = models.CharField(max_length=120, blank=True, null=True)
city = models.CharField(max_length=60, blank=True, null=True)
state = models.CharField(max_length=60, blank=True, null=True)
zipcode = models.CharField(max_length=5, blank=True, null=True)
description = models.TextField(blank=True, null=True)
objects = models.GeoManager()
rating = RatingField(range=5,weight=5,can_change_vote=True)
business_size = models.ManyToManyField("BusinessSize", related_name="software-business-size")
review = generic.GenericRelation("Review")
def __unicode__(self):
#return "%s %s %s"%(self.title, self.point.x, self.point.y)
return "%s"%(self.title)
#models.permalink
def get_absolute_url(self):
return ('software-detail', (),
{
'slug' :self.slug,
})
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.title)
super(Software, self).save(*args, **kwargs)
def natural_key(self):
return (self.title, self.developer)
class Meta:
unique_together = (('title', 'developer'),)
class SoftwareImage(models.Model):
software = models.ForeignKey(Software, related_name="softwareimages")
image = models.ImageField(upload_to='software/%Y/%m/%d', default='static/img/no-thumb.jpg', null=True, blank=True)
Views.py
def software_list_ajax_all(request):
result = Software.objects.all().select_related()
result = serializers.serialize('json', result, relations=('software',), extras=('__unicode__','get_absolute_url') )
return HttpResponse(result, content_type="application/json")
Json Response
1: Object
extras: Object
__unicode__: "test "
get_absolute_url: "/software/test/"
__proto__: Object
fields: Object
address: "test "
business_size: Array[1]
city: "test"
description: ""
developer: "test"
rating_score: 0
rating_votes: 0
slug: "test"
state: "CA "
submitted_by: Array[0]
title: "Comcash "
zipcode: "test"
__proto__: Object
model: "softwareapp.software"
pk: 3
Here is the solution. Basically I ended up using Django Rest framework. Custom relational fields feature of DRF enables you to create serializer relations.
class SoftwareImageField(serializers.RelatedField):
def to_native(self, value):
return '%s' % (value.image.url)
class SoftwareSerializer(serializers.HyperlinkedModelSerializer):
absurl = serializers.Field(source='get_absolute_url')
softwareimages = SoftwareImageField(many=True)
class Meta:
model = Software
fields = ('url','title','developer','absurl','slug','softwareimages')
Views.py
def software_list_ajax_all(request):
result = Software.objects.all()
serializer = SoftwareSerializer(result)
return JSONResponse(serializer.data)