How to save Parent django model in related model save - python

Have two models - Program and Segments. I need to calculate the total times in the program entry from the fields within the associated Segments. I attempted to do that by overriding the save methods, but when entering a new segment it won't update the program model entries unless I go directly into the program form and save/update it.
I am missing how to get the segment Update to cause the Program Save/Update to happen.
How do I give it the context to call the program save method within the Segment update (After the segment has been saved).
Code of the models is:
from django.db import models
from django.urls import reverse
from datetime import datetime, timedelta
class Program(models.Model):
air_date = models.DateField(default="0000-00-00")
air_time = models.TimeField(default="00:00:00")
service = models.CharField(max_length=10)
block_time = models.TimeField(default="00:00:00")
block_time_delta = models.DurationField(default=timedelta)
running_time = models.TimeField(default="00:00:00")
running_time_delta = models.DurationField(default=timedelta)
remaining_time = models.TimeField(default="00:00:00")
remaining_time_delta = models.DurationField(default=timedelta)
title = models.CharField(max_length=190)
locked_flag = models.BooleanField(default=False)
deleted_flag = models.BooleanField(default=False)
library = models.CharField(null=True,max_length=190,blank=True)
mc = models.CharField(null=True,max_length=64)
producer = models.CharField(null=True,max_length=64)
editor = models.CharField(null=True,max_length=64)
remarks = models.TextField(null=True,blank=True)
audit_time = models.DateTimeField(null=True)
audit_user = models.CharField(null=True,max_length=32)
def calculate_time(self):
total_run_time_delta = timedelta(minutes=0)
for segs in self.segments.all():
total_run_time_delta += segs.length_time_delta
self.running_time_delta = total_run_time_delta
self.running_time = f"{self.running_time_delta}"
hold_time = self.block_time.strftime("%H:%M:%S")
t = datetime.strptime(hold_time,"%H:%M:%S")
self.block_time_delta = timedelta(hours=t.hour,
minutes=t.minute,seconds=t.second)
self.remaining_time_delta = self.block_time_delta - total_run_time_delta
self.remaining_time = f"{abs(self.remaining_time_delta)}"
def save(self, *args, **kwargs):
self.calculate_time()
super().save(*args,**kwargs)
def __str__(self):
return f"{self.pk} : {self.title}"
def get_absolute_url(self):
return reverse('program_detail', args=[str(self.id)])
#return reverse('program-update', kwargs={'pk': self.pk})
class Segment(models.Model):
program_id = models.ForeignKey(Program,
on_delete=models.CASCADE,
related_name='segments', #new link to Program
)
sequence_number = models.DecimalField(decimal_places=2,max_digits=6,default="0.00")
title = models.CharField(max_length=190)
bridge_flag = models.BooleanField(default=False)
length_time = models.TimeField(null=True,default=None, blank=True)
length_time_delta = models.DurationField(default=timedelta)
author = models.CharField(max_length=64,null=True,default=None,blank=True)
voice = models.CharField(max_length=64,null=True,default=None,blank=True)
library = models.CharField(max_length=190,null=True,default=None,blank=True)
summary = models.TextField()
audit_time = models.DateTimeField(null=True)
audit_user = models.CharField(null=True,max_length=32)
def save( self, *args, **kwargs):
super().save(*args,**kwargs)
return super(Program,self.program_id).save()
def __str__(self):
return f"{self.title}"
The views look like this...
class ProgramUpdateView(LoginRequiredMixin,UpdateView):
class Meta:
model = Program
widgets = {
'remarks': Textarea(attrs={'row':10, 'cols':80}),
}
model = Program
success_url = "/program/{id}/"
template_name = 'program_update.html'
fields = [
'title',
'service',
'library',
'air_date',
'air_time',
'producer',
'editor',
'mc',
'block_time',
'remaining_time',
'running_time',
'remarks',
]
def form_valid(self, form):
return super(ProgramUpdateView, self).form_valid(form)
class SegmentUpdate(LoginRequiredMixin,UpdateView):
model = Segment
fields = '__all__'
template_name = 'segment_update.html'
I originally thought I could do this all in the models, but now I am not so sure .
Thanks for any info you can provide....

try to directly call Program.save() method through the fk
in Segment model
def save( self, *args, **kwargs):
super().save(*args,**kwargs)
self.program_id.save()
or use django signals https://docs.djangoproject.com/en/3.1/topics/signals/
from django.db.models.signals import post_save, post_delete
#receiver([post_save, post_delete], sender=Segment)
def update_program(sender, instance, **kwargs):
program = Program.objects.get(pk=instance.program_id.pk)
program.save()

Please keep your database atomic. Don't save in it something that can be computed from other fields unless you have a very good reason to do it. The reason you're giving for doing it doesn't seem like a good one.
You want the total time of the segments when you got a list of programs ? Fine, simply annotate the querystring with a sum. You'll do it everytime ? Create a custom queryset/manager that do it for you.

Related

How to display recent posts in django

Here is the case, I need the last records of the model to be displayed on the page, for this I added a new pub_date record in the models to add to the queue of records, I also added this to views.py, and it kind of displays, but both records.
views.py code
class IndexView(generic.ListView):
template_name = 'Homepage/index.html'
model = Goods
context_object_name = 'goods'
def description(self):
return self.description_text
def price(self):
return self.price_text
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
numbers = Number.objects.all()
context['numbers'] = numbers
return context
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
avaibilitys = Avaibility.objects.order_by('-pub_date')
context['avaibilitys'] = avaibilitys
return context
models.py code
class Avaibility(models.Model):
name_text = models.CharField(max_length=200)
apply_text = models.CharField(max_length=200)
presence_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published', null=True)
def __str__(self):
return self.name_text
def __str__(self):
return self.apply_text
def __str__(self):
return self.presence_text
this is what displays
You are just sorting the data using order_by and assign the sorted data to a variable:
avaibilitys = Avaibility.objects.order_by('-pub_date')
If you want to get only one of them, you can do this:
avaibilitys = Avaibility.objects.order_by('-pub_date').first()
EDIT
If you want the last one, do this:
avaibilitys = Avaibility.objects.order_by('-pub_date').last()

Django rest framework, update object after creation

I have a DRF API that takes in the following model:
class Points(models.Model):
mission_name = models.CharField(name='MissionName',
unique=True,
max_length=255,
blank=False,
help_text="Enter the mission's name"
)
# Some irrlevant feid
url = models.URLField(help_text='Leave Empty!', default=" ")
date_added = models.DateTimeField(default=timezone.now)
class Meta:
get_latest_by = 'date_added'
And it's serializer:
from rest_framework.serializers import HyperlinkedModelSerializer
from .models import Points
class PointsSerializer(HyperlinkedModelSerializer):
class Meta:
model = Points
fields = (
'id', 'MissionName', 'GDT1Latitude', 'GDT1Longitude',
'UavLatitude', 'UavLongitude', 'UavElevation', 'Area',
'url', 'date_added'
)
And the view:
class PointsViewSet(ModelViewSet):
# Return all order by id, reversed.
queryset = Points.objects.all().order_by('-id')
serializer_class = PointsSerializer
data = queryset[0]
serialized_data = PointsSerializer(data, many=False)
points = list(serialized_data.data.values())
def retrieve(self, request, *args, **kwargs):
print(self.data)
mission_name = self.points[1]
assign_gdt = GeoPoint(lat=self.points[2], long=self.points[3])
gdt1 = [assign_gdt.get_lat(), assign_gdt.get_long()]
assign_uav = GeoPoint(lat=self.points[4], long=self.points[5], elevation=self.points[6])
uav = [assign_uav.get_lat(), assign_uav.get_long(), assign_uav.get_elevation()]
area_name = f"'{self.points[-2]}'"
main = MainApp.run(gdt1=gdt1, uav=uav, mission_name=mission_name, area=area_name)
print('file created')
return render(request, main)
I want to update the URL field of the file to contain a constant pattern and format in the end the mission_name field.
object.url = f'127.0.0.1/twosecondgdt/{mission_name}'
How can that be achieved and where should I store such code, the views.py or serializers.py?
There are several ways this could be achieved based on your requirements.
If you want to set the url upon creation even if it is not through the api, you can do it in the save method of the model itself:
class Points(models.Model):
# fields here
def save(self, **args, **kwargs):
if not self.url.strip():
# You may want to store the value of `127...` in an environment variable
self.url = f"127.0.0.1/twosecondgdt/{self.mission_name}"
super().save(*args, **kwargs)
If you want to set it through the view/serializer, you can set it in the create method of your serializer:
class PointsSerializer(HyperlinkedModelSerializer):
def create(self, validated_data):
mission_name = validated_data["mission_name"]
validated_data["url"] = f"127.0.0.1/twosecondgdt/{mission_name}"
return super().create(validated_data)
You can also override some methods in your viewset like perform_create or create

Django-Haystack not returning exact query

I'm trying to fix my Django-haystack combined with Elasticsearch search results to be exact.
The problem I have now is that when a user try for example, the "Mexico" query, the search results also returns deals in "Melbourne" which is far from being user-friendly.
Anyone can help me to fix this problem?
This is what I've tried so far but no good results:
My forms.py
from haystack.forms import FacetedSearchForm
from haystack.inputs import Exact
class FacetedProductSearchForm(FacetedSearchForm):
def __init__(self, *args, **kwargs):
data = dict(kwargs.get("data", []))
self.ptag = data.get('ptags', [])
self.q_from_data = data.get('q', '')
super(FacetedProductSearchForm, self).__init__(*args, **kwargs)
def search(self):
sqs = super(FacetedProductSearchForm, self).search()
# Ideally we would tell django-haystack to only apply q to destination
# ...but we're not sure how to do that, so we'll just re-apply it ourselves here.
q = self.q_from_data
sqs = sqs.filter(destination=Exact(q))
print('should be applying q: {}'.format(q))
print(sqs)
if self.ptag:
print('filtering with tags')
print(self.ptag)
sqs = sqs.filter(ptags__in=[Exact(tag) for tag in self.ptag])
return sqs
My search_indexes.py
import datetime
from django.utils import timezone
from haystack import indexes
from haystack.fields import CharField
from .models import Product
class ProductIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.EdgeNgramField(
document=True, use_template=True,
template_name='search/indexes/product_text.txt')
title = indexes.CharField(model_attr='title')
description = indexes.EdgeNgramField(model_attr="description")
destination = indexes.EdgeNgramField(model_attr="destination") #boost=1.125
link = indexes.CharField(model_attr="link")
image = indexes.CharField(model_attr="image")
# Tags
ptags = indexes.MultiValueField(model_attr='_ptags', faceted=True)
# for auto complete
content_auto = indexes.EdgeNgramField(model_attr='destination')
# Spelling suggestions
suggestions = indexes.FacetCharField()
def get_model(self):
return Product
def index_queryset(self, using=None):
"""Used when the entire index for model is updated."""
return self.get_model().objects.filter(timestamp__lte=timezone.now())
My models.py
class Product(models.Model):
destination = models.CharField(max_length=255, default='')
title = models.CharField(max_length=255, default='')
slug = models.SlugField(unique=True, max_length=255)
description = models.TextField(max_length=2047, default='')
link = models.TextField(max_length=500, default='')
ptags = TaggableManager()
image = models.ImageField(max_length=500, default='images/zero-image-found.png')
timestamp = models.DateTimeField(auto_now=True)
def _ptags(self):
return [t.name for t in self.ptags.all()]
def get_absolute_url(self):
return reverse('product',
kwargs={'slug': self.slug})
def save(self, *args, **kwargs):
if not self.id:
self.slug = slugify(self.title)
super(Product, self).save(*args, **kwargs)
def __str__(self):
return self.destination
And what I have in my views.py
from haystack.generic_views import FacetedSearchView as BaseFacetedSearchView
from .forms import FacetedProductSearchForm
class FacetedSearchView(BaseFacetedSearchView):
form_class = FacetedProductSearchForm
facet_fields = ['ptags']
template_name = 'search_result.html'
paginate_by = 30
context_object_name = 'object_list'
Thank you.
I just found the solution to this problem. Please listen up if you want to avoid loosing any of you reputations by placing a bounty on the same problem.
Basically I had to replace my original destination field in my search_indexes.py document to the following line:
From this: destination = indexes.EdgeNgramField(model_attr="destination")
To this: destination = indexes.CharField(model_attr="destination")
Your issue is in your use of dict.get
self.q_from_data = data.get('q', [''])[0]
For example
data.get('q') # This will return the string "Mexico"
data.get('q')[0] # This will return the first letter "M"
The line should be
self.q_from_data = data.get('q', '')

Making Django-haystack autocomplete suggestions work for accented query (à, é, ï, etc..)

I'm trying to make the suggestions from Django-haystack' autocomplete to be sensitive to words containing accents. (For french language)
Current result:
User type Seville
Output suggestion returns nothing because the actual destination name is Séville
Expected result:
User type Seville
Output suggestion returns Séville
I have read the following documentation but I'm still unsure on how to achieve this: https://django-haystack.readthedocs.io/en/master/searchqueryset_api.html#order-by
Here's my code:
Forms.py
from haystack.forms import FacetedSearchForm
from haystack.inputs import Exact
class FacetedProductSearchForm(FacetedSearchForm):
def __init__(self, *args, **kwargs):
data = dict(kwargs.get("data", []))
self.ptag = data.get('ptags', [])
self.q_from_data = data.get('q', '')
super(FacetedProductSearchForm, self).__init__(*args, **kwargs)
def search(self):
sqs = super(FacetedProductSearchForm, self).search()
# Ideally we would tell django-haystack to only apply q to destination
# ...but we're not sure how to do that, so we'll just re-apply it ourselves here.
q = self.q_from_data
sqs = sqs.filter(destination=Exact(q))
print('should be applying q: {}'.format(q))
print(sqs)
if self.ptag:
print('filtering with tags')
print(self.ptag)
sqs = sqs.filter(ptags__in=[Exact(tag) for tag in self.ptag])
return sqs
search_indexes.py
import datetime
from django.utils import timezone
from haystack import indexes
from haystack.fields import CharField
from .models import Product
class ProductIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.EdgeNgramField(
document=True, use_template=True,
template_name='search/indexes/product_text.txt')
title = indexes.CharField(model_attr='title')
description = indexes.EdgeNgramField(model_attr="description")
destination = indexes.EdgeNgramField(model_attr="destination") #boost=1.125
link = indexes.CharField(model_attr="link")
image = indexes.CharField(model_attr="image")
# Tags
ptags = indexes.MultiValueField(model_attr='_ptags', faceted=True)
# for auto complete
content_auto = indexes.EdgeNgramField(model_attr='destination')
# Spelling suggestions
suggestions = indexes.FacetCharField()
def get_model(self):
return Product
def index_queryset(self, using=None):
"""Used when the entire index for model is updated."""
return self.get_model().objects.filter(timestamp__lte=timezone.now())
Models.py
class Product(models.Model):
destination = models.CharField(max_length=255, default='')
title = models.CharField(max_length=255, default='')
slug = models.SlugField(unique=True, max_length=255)
description = models.TextField(max_length=2047, default='')
link = models.TextField(max_length=500, default='')
ptags = TaggableManager()
image = models.ImageField(max_length=500, default='images/zero-image-found.png')
timestamp = models.DateTimeField(auto_now=True)
def _ptags(self):
return [t.name for t in self.ptags.all()]
def get_absolute_url(self):
return reverse('product',
kwargs={'slug': self.slug})
def save(self, *args, **kwargs):
if not self.id:
self.slug = slugify(self.title)
super(Product, self).save(*args, **kwargs)
def __str__(self):
return self.destination
Finally, in my views.py:
from haystack.generic_views import FacetedSearchView as BaseFacetedSearchView
from .forms import FacetedProductSearchForm
from haystack.query import SearchQuerySet
def autocomplete(request):
sqs = SearchQuerySet().autocomplete(
content_auto=request.GET.get('query',''))[:5]
destinations = {result.destination for result in sqs}
s = [{"value": dest, "data": dest} for dest in destinations]
output = {'suggestions': s}
return JsonResponse(output)
class FacetedSearchView(BaseFacetedSearchView):
form_class = FacetedProductSearchForm
facet_fields = ['ptags']
template_name = 'search_result.html'
paginate_by = 30
context_object_name = 'object_list'
Any ideas on how to achieve this?

How to customize Model or Manager that change the data format in django?

I hava an Article model ,contains a title column,which can be stored mix with white space,what i want is that ,every time i query an article,space in title content could be repaced with dash,for url friendly.
models.py:
class Article(models.Model):
STATUS = (
(0,'on'),
(1,'off')
)
#id = models.IntegerField(primary_key=True,help_text='primary key',auto_created=True)
category = models.ForeignKey(Category,related_name='articles', help_text='foreigner key reference Category')
#author = models.ForeignKey(myadmin.User, help_text='foreigner key reference myadmin User')
title = models.CharField(max_length=100, help_text='article title')
description = models.TextField(help_text='article brief description')
content = models.TextField(help_text='article content')
like = models.IntegerField(default=0,help_text='like numbers')
secretcode = models.CharField(max_length=512,help_text='who has the code can scan')
status = models.IntegerField(choices=STATUS,help_text='status of the article')
createtime = models.DateTimeField(auto_now_add=True,help_text='time that first created')
modifytime = models.DateTimeField(auto_now=True,help_text='time when modified')
articles = models.Manager()
def __str__(self):
return self.title
class Meta:
db_table = 'article'
my view.py:
def get(self,request):
offset = int(request.GET.get('offset', 0))
category = request.GET.get('category')
end = offset+10
articlecatalogs = Article.articles.filter(category__name=category)[offset:end]
i was thinking creating a custom Manager and define a method to transform the data,but the query conditions needed are from request,in here,i don't know how to do it ?can someone help me?
I think you have to use slug filed as well and overwrite your save method for save slug something like this :
class Article(models.Model):
slug = models.SlugField("slug")
category = models.ForeignKey(Category,related_name='articles', help_text='foreigner key reference Category')
-- more fields --
def __str__(self):
return self.title
def get_absolute_url(self):
return self.slug
def save(self, *args, **kwargs):
if not self.slug:
self.slug = self.title.strip(" ").replace(' ','-')
super(Article, self).save(self, *args, **kwargs)
#property
def get_title(self):
""" write python code for remove extra spaces so can can dispay your tile in html and call this method with instance when you want to print title """
return new_title_without_extra_spaces
for details page you can use slug value for get a instance. Hope this would be helpful to you.

Categories

Resources