Calculating values from different Django Models - django

I have 2 similar django models, the difference between them is that one of them has a Foreign key with another model and the other is like a general Model.
class Project:
name = models.CharField(default='',max_length=100,verbose_name="name")
class Article(models.Model):
code = models.CharField(default='',max_length=20,verbose_name="Norma")
name = models.TextField(default='',verbose_name="Denumire")
project = models.ForeignKey(Project,on_delete=models.CASCADE,null=True)
quantity = models.FloatField(default=0,verbose_name="Quantity")
value = models.FloatField(default=0,verbose_name="Value")
class BaseArticle(models.Model):
code = models.CharField(default='',max_length=20,verbose_name="Norma")
name = models.TextField(default='',verbose_name="Denumire")
price = models.FloatField(default=0,verbose_name="Price")
I want to calculate the value attribute from Article model, it should be like this:
if article.code == basearticle.code:
article.value = article.quantiy * basearticle.price
How should I do this type of calculations? Should I write the logic in views.py?
views.py:
class ProjectDetail(LoginRequiredMixin, DetailView):
template_name = "proiecte/project_detail.html"
context_object_name = "projects"
model = Project
In the template I use {% for article in projects.article_set.all %} to generate a table that has all the attributes as headers.

ok based on your understanding you can do this way
class Article(models.Model):
code = models.CharField(default='',max_length=20,verbose_name="Norma")
name = models.TextField(default='',verbose_name="Denumire")
project = models.ForeignKey(Project,on_delete=models.CASCADE,null=True)
quantity = models.FloatField(default=0,verbose_name="Quantity")
value = models.FloatField(default=0,verbose_name="Value")
def clean(self):
values =Article.objects.get(code='P1').quantity*BaseArticle.objects.get(code =
Article.objects.get(code='P1')
return value
Since you can access your clean method anywhere as it is instance of class in views
class ProjectDetail(LoginRequiredMixin, DetailView):
template_name = "proiecte/project_detail.html"
context_object_name = "projects"
instance = Article.objects.get(pk=id)
value =instance.clean()
# your logic

Related

Doing a Multi-step form with Django Class-based CreateView

I have multiple class-based createviews. My goal is to link all createviews such that when I post the first createview, it will redirect me to the second createview where it will retrieve the data entered from first createview.
Does anyone know the solution to this?
The first createview (Step01) contains django-inline-formset-factory code which is similar to this code, while the rest (Step02 and Step03) contain basic code.
I have referred to this link, but the solution is based on using function-based view. Have also made attempt using Django's Form-Wizard, but handling django-inline-formset with it is still too complicated for me.
Why do I do this? So that each group level (i.e. general, staff, managerial) can access their respective view but not the view for other group level.
This is my current code:
models.py
class Model_Step01_Cart(models.Model):
cart_name = models.CharField(max_length=100)
class Model_Step01_CartItem(models.Model):
cart = models.ForeignKey(Profile)
item_name = models.CharField(max_length = 100)
item_quantity = models.FloatField(null = True, blank = True)
class Model_Step02_Staffnote(models.Model):
note_staff = models.TextField(max_length = 500, null = True, blank = True)
class Model_Step03_Managernote(models.Model):
note_manager = models.TextField(max_length = 500, null = True, blank = True)
forms.py
class Form_Step01_Cart(forms.ModelForm):
class Meta:
model = Model_Step01_Cart
fields = ["cart_name"]
class Form_Step01_CartItem(forms.ModelForm):
class Meta:
model = Model_Step01_CartItem
fields = ["cart", "item_name", "item_quantity"]
Formset_CartItem = forms.inlineformset_factory(
Model_Step01_Cart,
Model_Step01_CartItem,
form = Form_Step01_CartItem,
extra = 3
)
class Form_Step02(forms.ModelForm):
class Meta:
model = Model_Step02_Staffnote
fields = ["note_staff"]
class Form_Step03(forms.ModelForm):
class Meta:
model = Model_Step03_Managernote
fields = ["note_manager"]
views.py
class View_Step01(CreateView):
# contains formset_factory for "Model_Step01_Cart" and "Model_Step01_CartItem"
model = Model_Step01_Cart
fields = ["cart_name"]
# similar with the code displays here: https://medium.com/#adandan01/django-inline-formsets-example-mybook-420cc4b6225d
class View_Step02(CreateView):
# gets info from step 01, and adds staff's note
class View_Step03(CreateView):
# gets info from step 01 and 02, and adds manager's note.
Find my answer to split a single form across multiple views. You can configure the gist script to fit your requirement
For class-based-view map the success url of one view to other CreateView.as_view(model=myModel, success_url="/<path-to-view2/")

Include values of different table in django models

I'm trying to check if files reported in one system exist in a different system. The models belong to different tables in different databases. They have no relationship other than the name in common.
I'm using django rest framework to serialize the values of one table and I'd like to include the values of the other table in one efficient way. The way I'm currently doing, makes too many queries! My question is: Is there a way to improve this performance issue?
Here is an example of what I have
# model1
class Product(models.Model):
name = models.CharField(max_length=50)
# model2 (different database)
class Files(models.Model):
name = models.CharField(max_length=50)
filename = models.CharField(max_length=50)
And my view set is
class ProductViewSet(viewsets.ModelViewSet):
queryset = Inventory.objects.all()
serializer_class = ProductSerializer
I managed to get the result as I said (not efficiently, though) in two different ways:
1) Including the fields in the serializer
class ProductSerializer(serializers.ModelSerializer):
has_png = serializers.SerializerMethodField('has_png')
has_jpg = serializers.SerializerMethodField('has_jpg')
def has_png(self, product):
# I wish I could avoid this code duplication too...
# I'm basically using the same code in two functions
files = Files.objects.filter(name=product.name)
filtered_files = files.filter(filename__startswith='png')
return filtered_files.exists()
def has_bam(self, product):
files = Files.objects.filter(name=product.name)
filtered_files = files.filter(filename__istartswith='jpg')
return filtered_files.exists()
Meta:
model = Product
2) Including properties in my model1 which is serialized
class Product(modes.Model):
name = models.CharField(max_length=50)
def _get_png(self):
# I tried to cache this query in a different function but didn't work
files = Files.objects.filter(name=self.name)
filtered_files = files.filter(filename__istartswith='png')
return filtered_files.exists()
def _get_jpg(self):
files = Files.objects.filter(name=self.name)
filtered_files = files.filter(filename__istartswith='jpg')
return filtered_files.exists()
has_png = property(_get_png)
has_jpg = property(_get_jpg)
and then I add in the serializer:
class ProductSerializer(serializers.ModelSerializer):
has_fastq = serializers.ReadOnlyField()
has_bam = serializers.ReadOnlyField()
Meta:
model = Product
you can try to use cached_property
from django.db.models.functions import Lower
from django.utils.functional import cached_property
class Product(modes.Model):
name = models.CharField(max_length=50)
#cached_property
def file_formats(self):
files = Files.objects.filter(name=self.name)
files = files.annotate(lower_format=Lower('file_format')))
return files.values_list('lower_format', flat=True)
def _get_png(self):
return 'png' in self.file_formats
def _get_jpg(self):
return 'jpg' in self.file_formats
has_png = property(_get_png)
has_jpg = property(_get_jpg)

Fetching nested objects with Django REST framework

I am attempting to fetch nested objects but not quite sure how to achieve this. My model is as shown:
class Brand(models.Model)
name = models.CharField(max_length=128)
class Size(models.Model)
name = models.CharField(max_length=128)
class Brand_Size(models.Model)
brand = models.ForeignKey(Brand)
size = models.ForeignKey(Size)
class Brand_Size_Location(models.Model)
location = models.ForeignKey(Location)
brand_size = models.ForeignKey(Brand_Size)
I filter objects in Brand_Size_Location by location which can occur 1..x. I want my serializer to output the results in terms of the model Brand (BrandSerializer). Since my resultset can be 1..x and furthermore the occurrence of Brand can be duplicates i would like to eliminate these aswell at the same time.
You should be able to do this fairly easily by including a serializer field in your BrandSerializer:
class BrandSerializer(serializers.ModelSerializer):
brand_sizes = BrandSizeSerializer(
source='brand_size_set',
read_only=True
)
class Meta:
model = Brand
fields = (
'id',
...add more fields you want here
'brand_sizes')
You can simlarly create the brand size serializer to nest the locations
Filtering on this relationship occurs in the view and will need a custom filter. Here's a basic example using a ListView:
class BrandFilter(django_filters.FilterSet):
location = django_filters.ModelMultipleChoiceFilter(
queryset=Brand_Size_Location.objects.all(),
name='brand_size__brand_size_location__location'
)
location_name = django_filters.CharFilter(
name='brand_size__brand_size_location__location__name'
)
class Meta:
model = Brand
fields = [
'location',
'location_name'
]
class BrandList(LoginRequiredMixin, generics.ListCreateAPIView):
model = Brand
serializer_class = BrandSerializer
filter_class = BrandFilter
You can then use query parameters to filter on the URL like:
http://somehost/api/brands/?location=42
which uses a PK to filter, or with the location name (assuming you have a name field in the Location model):
http://somehost/api/brands/?location_name=Atlantis

formset_factory with forms with different initial data

I am writing a view that uses POST data to display multiple forms with differing prefilled FK's
I have a ModelForm in forms.py
class SurveyForm(forms.ModelForm):
class Meta:
model = Survey
who's model looks like this...
class Survey(models.Model):
student = models.ForeignKey(Student)
surveyset = models.ForeignKey(SurveySet)
cei_0 = models.BooleanField()
cei_1 = models.BooleanField()
My view looks kind of like this so far
# ... after building a list from POST we essentially have:
list_of_studentids = [1,3,2,6,7,45]
students = []
for i in list_of_student_ids:
students.append(Student.objects.filter(id=i))
SurveyFormSet = formset_factory(SurveyForm, extra=6)
formset = SurveyFormSet(initial=[
{'surveyset': SurveySet.create(),
'student': ?????,}
])
How do I return a bunch of forms with different student FK's and the same surveyset FK?
You need to pass an instance attribute to the form:
prefilled_survey = Survey(student=student_instance, surveyset=surveyset_instance)
form = SurveyForm(request.POST or None, instance=prefilled_survey)

Django filters - Using an AllValuesFilter (with a LinkWidget) on a ManyToManyField

This is my first Stack Overflow question, so please let me know if I do anything wrong.
I wish to create an AllValues filter on a ManyToMany field using the wonderful django-filters application. Basically, I want to create a filter that looks like it does in the Admin, so I also want to use the LinkWidget too.
Unfortunately, I get an error (Invalid field name: 'operator') if I try this the standard way:
# Models
class Organisation(models.Model):
name = models.CharField()
...
class Sign(models.Model):
name = models.CharField()
operator = models.ManyToManyField('Organisation', blank=True)
...
# Filter
class SignFilter(LinkOrderFilterSet):
operator = django_filters.AllValuesFilter(widget=django_filters.widgets.LinkWidget)
class Meta:
model = Sign
fields = ['operator']
I got around this by creating my own filter with the many to many relationship hard coded:
# Models
class Organisation(models.Model):
name = models.CharField()
...
class Sign(models.Model):
name = models.CharField()
operator = models.ManyToManyField('Organisation', blank=True)
...
# Filter
class MyFilter(django_filters.ChoiceFilter):
#property
def field(self):
cd = {}
for row in self.model.objects.all():
orgs = row.operator.select_related().values()
for org in orgs:
cd[org['id']] = org['name']
choices = zip(cd.keys(), cd.values())
list.sort(choices, key=lambda x:(x[1], x[0]))
self.extra['choices'] = choices
return super(AllValuesFilter, self).field
class SignFilter(LinkOrderFilterSet):
operator = MyFilter(widget=django_filters.widgets.LinkWidget)
I am new to Python and Django. Can someone think of a more generic/elegant way of doing this?
Why did you subclass LinkOrderFilterSet?
Maybe the connect way to use it is this:
import django_filters
class SignFilter(django_filters.FilterSet):
operator = django_filters.AllValuesFilter(widget=django_filters.widgets.LinkWidget)
class Meta:
model = Sign
fields = ['operator']
You can use this
class CircleFilter(django_filters.FilterSet):
l = []
for c in Organisation.objects.all():
l.append((c.id, c.name))
operator = django_filters.ChoiceFilter(
choices=set(l))
class Meta:
model = Sign
fields = ['operator']