Autofilling Django model form field with data from associated objects - django

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)

Related

How to filter in Django Rest Framework function based view?

So many documentation for filtering in Django rest framework but all the examples are in class based view. but I am trying to do the same in DRF function based view. I wanted to do multiple filter for my items queryset.
I tried one way and it is working perfectly. Here first I am trying to search by item name or restaurant name in one request. then I take another keyword and try to filter restaurant name or item name based on restaurant city. It is working perfectly like if I hit this url
http://localhost:8000/api/items/?keyword=lasagne&keyword1=paris
then it gives me the perfect response.
But What I am asking for is that now my code looks for this specific part is messy and I want to add more fields for multiple filtering. Which procedure to follow? Should I follow this one and multiple requests and trying to filter from this.
Suppose now I want to filter the queryset based on dish_type, price, item_type, location and then search for items by name or restaurants by name
#this is my models
class Restaurant(models.Model):
user = models.OneToOneField(CustomUser, related_name='restaurant', on_delete=models.CASCADE, null=False)
name = models.CharField(max_length=200, blank=True, null=True)
profile_picture = models.ImageField(null=True, blank=True)
address = models.TextField(max_length=2000, blank=True, null=True)
city = models.CharField(max_length=200)
latitude = models.DecimalField(max_digits = 13, decimal_places = 7, blank=True, null=True)
longitude = models.DecimalField(max_digits = 13, decimal_places = 7, blank=True, null=True)
is_verified = models.BooleanField(default=False)
createdAt = models.DateTimeField(auto_now_add=True)
def __str__(self):
return str(self.name)
class Item(models.Model):
user = models.ForeignKey(Restaurant, on_delete=models.CASCADE)
name = models.CharField(max_length=220)
image = models.ImageField(null=True, blank=True)
dish_type = models.ForeignKey(Dishtype, on_delete=models.CASCADE)
item_type = models.ForeignKey(Itemtype, on_delete=models.CASCADE)
description = models.TextField(max_length=10000)
rating = models.DecimalField(max_digits=7, decimal_places=2, blank=True, null=True)
numReviews = models.IntegerField(null=True, blank=True, default=0)
old_price = models.DecimalField(max_digits=11, decimal_places=2)
discount = models.IntegerField(blank=True, null=True)
price = models.DecimalField(max_digits=12, decimal_places=2, blank=True, null=True)
countInStock = models.IntegerField(blank=True, null=True, default=0)
createdAt = models.DateTimeField(auto_now_add=True)
_id = models.AutoField(primary_key=True, editable=False)
def save(self, *args, **kwargs):
self.price = Decimal(self.old_price * (100 - self.discount) / 100)
return super(Item, self).save(*args, **kwargs)
class Meta:
ordering = ['-createdAt']
def __str__(self):
return self.name
#serializer
class RestaurantSerializer(serializers.ModelSerializer):
user = serializers.SerializerMethodField(read_only=True)
class Meta:
model = Restaurant
fields = '__all__'
def get_user(self, obj):
user = obj.user
serializer = UserSerializer(user, many=False)
return serializer.data
class ItemSerializer(serializers.ModelSerializer):
user = serializers.SerializerMethodField(read_only=True)
dish_type = serializers.SerializerMethodField(read_only=True)
item_type = serializers.SerializerMethodField(read_only=True)
class Meta:
model = Item
fields = '__all__'
def get_user(self, obj):
user = obj.user
serializer = RestaurantSerializer(user, many=False)
return serializer.data
def get_dish_type(self, obj):
dish_type = obj.dish_type
serializer = DishtypeSerializer(dish_type, many=False)
return serializer.data
def get_item_type(self, obj):
item_type = obj.item_type
serializer = ItemtypeSerializer(item_type, many=False)
return serializer.data
#views.py
#api_view(['GET'])
#permission_classes([IsAuthenticated])
def getItems(request):
user = request.user
query = request.query_params.get('keyword')
if query == None:
query = ''
cuery = request.query_params.get('keyword1')
if cuery == None:
cuery = ''
items = Item.objects.select_related('user').select_related('dish_type').select_related('item_type').all().filter(
Q(name__icontains = query) | Q(user__name__icontains = query))
else:
restaurant_city = Item.objects.select_related('user').select_related('dish_type').select_related('item_type').all(
).filter(Q(user__city__iexact = cuery))
items = restaurant_city.filter(Q(name__icontains = query) | Q(user__name__icontains = query))
serializer = ItemSerializer(items, many=True)
return Response(serializer.data)
######Updated solved the problem
#filters.py
class ItemFilter(django_filters.FilterSet):
numReviews = django_filters.NumberFilter()
numReviews__gt = django_filters.NumberFilter(field_name='numReviews', lookup_expr='gt')
numReviews__lt = django_filters.NumberFilter(field_name='numReviews', lookup_expr='lt')
name = django_filters.CharFilter(lookup_expr='icontains')
class Meta:
model = Item
fields = ['_id', 'dish_type__id']
#views
#api_view(['GET'])
#permission_classes([IsAuthenticated])
def getItems(request):
user = request.user
queryset = Item.objects.all()
filterset = ItemFilter(request.GET, queryset=queryset)
if filterset.is_valid():
queryset = filterset.qs
serializer = ItemSerializer(queryset, many=True)
return Response(serializer.data)
now data are passing like this
http://localhost:8000/api/items/?numReviews__gt=20&numReviews__lt=22
You can use queryset and override get_queryset function.
class FooViewSet(GenericViewSet, mixins.ListModelMixin):
authentication_classes = [JSONWebTokenAuthentication]
permission_classes = [IsAuthenticated]
serializer_class = ItemSerializer
def get_queryset(self):
query = self.request.query_params.get('keyword', '')
if not self.request.query_params.get('keyword1'):
items = Item.objects.select_related('user').select_related('dish_type').select_related(
'item_type').all().filter(
Q(name__icontains=query) | Q(user__name__icontains=query))
else:
restaurant_city = Item.objects.select_related('user').select_related('dish_type').select_related(
'item_type').all(
).filter(Q(user__city__iexact=self.request.query_params.get('keyword1', '')))
items = restaurant_city.filter(Q(name__icontains=query) | Q(user__name__icontains=query))
return items

Django - Form - ForeignKey - Hidden - Default value

I have a Hidden ForeignKey in an update form that I want to set to value of default value of 2 in my html form, but I can't get it to work.
forms.py
eval_sent_state = forms.ModelChoiceField(widget=forms.HiddenInput(), initial=2,queryset=models.EvalUrlSentState.objects.all())
The Html output i get:
<input type="hidden" name="eval_sent_state" value="1" id="id_eval_sent_state">
from views.py
class ClassSchoolTeacherUpdateView(generic.UpdateView):
model = models.ClassSchool
form_class = forms.ClassSchoolTeacherForm
pk_url_kwarg = "pk"
def get_object(self, queryset=None):
return models.ClassSchool.objects.get(class_random_key=self.kwargs.get("random"))
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['username'] = self.kwargs.get('username')
context['random'] = self.kwargs.get('random')
return context
from models.py:
class ClassSchool(models.Model):
# Relationships
eval_sent_state = models.ForeignKey("EvalUrlSentState", default=1, on_delete=models.SET_NULL, blank=True, null=True)
# Fields
class_name = models.CharField(max_length=100)
class_student_size = models.IntegerField(blank=True, null=True)
class_subject = models.CharField(max_length=100)
class_element_name = models.CharField(max_length=100)
class_teacher_user = models.CharField(max_length=100)
class_teacher_name = models.CharField(max_length=100, blank=True, null=True)
eval_year = models.IntegerField(default=2022)
class_random_key = models.CharField(max_length=8)
eval_url = models.CharField(max_length=400)
eval_open_datetime = models.DateTimeField(null=True, blank=True)
created = models.DateTimeField(auto_now_add=True, editable=False)
last_updated = models.DateTimeField(auto_now=True, editable=False)
You can override your form to always set the eval_sent_state field to the value you want in the save method, you should remove the field from the form fields though
class ClassSchoolTeacherForm(forms.ModelForm):
class Meta:
model = ClassSchool
exclude = ['eval_sent_state']
def save(self, *args, **kwargs):
self.instance.eval_sent_state_id = 2
return super().save(*args, **kwargs)

Django ModelChoiceField: filtering object based on pk in url

I've read many questions about this topic, but none of the methods work for me.
There are 3 related models:
class Trips(models.Model):
lake = models.CharField("Lake", max_length=150)
city = models.CharField("City", max_length=100, blank=True)
s_date = models.DateTimeField("Starting Date", auto_now=False, auto_now_add=False)
e_date = models.DateTimeField("Ending Date", auto_now=False, auto_now_add=False)
trip_id = models.AutoField(primary_key=True)
class Meta:
verbose_name = "Trip"
verbose_name_plural = "Trips"
def __str__(self):
return f"{self.lake}-{self.trip_id}-{self.s_date}"
class Fisherman(models.Model):
name = models.CharField("Fisherman", max_length=50)
trip = models.ForeignKey(Trips, on_delete=models.CASCADE)
fisherman_id = models.AutoField(primary_key=True)
class Meta:
verbose_name = "Fisherman"
verbose_name_plural = "Fishermen"
def __str__(self):
return f"{self.name}-{self.fisherman_id}"
class Catch(models.Model):
fish_type = models.CharField("Fish Type", max_length=50)
catch_id = models.AutoField(primary_key=True)
weight = models.DecimalField("Weight", max_digits=5, decimal_places=2)
length = models.DecimalField("Length", max_digits=5, decimal_places=2, blank=True, null=True)
datetime = models.DateTimeField("Catch Time", auto_now=False, auto_now_add=False)
fisherman = models.ForeignKey(Fisherman, on_delete=models.CASCADE)
trip = models.ForeignKey(Trips, on_delete=models.CASCADE)
class Meta:
verbose_name = "Catch"
verbose_name_plural = "Catches"
def __str__(self):
return f"{self.fish_type}-{self.catch_id}"
I have a ModelForm to create a new catch. Here I use a ModelChoiceField to list Fishermen, but I don't know how to filter them. I only want display those who belong to the trip.
class CatchForm(forms.ModelForm):
fisherman = forms.ModelChoiceField(queryset= Fisherman.objects.all())
class Meta:
model = Catch
fields = ["fish_type", "weight", "length", "datetime", "fisherman"]
widgets = {
"datetime": forms.DateTimeInput(format='%Y-%m-%d %H:%M', attrs={'class':'datetimefield form-control'}),
}
views.py
I' ve read that get_form_kwargs should be used in views to override fields in the form, but it didn't work for me.
class NewCatchView(CreateView):
model = Catch
form_class = CatchForm
template_name = "new_trip/new_catch.html"
# Probably, this is wrong
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['fisherman'] = Fisherman.objects.filter(trip=self.kwargs.get('pk'))
return kwargs
def form_valid(self, form):
form.instance.trip = Trips.objects.get(pk=self.kwargs['pk'])
return super().form_valid(form)
def get_success_url(self):
return reverse('new_trip:trip_details', args=(self.kwargs['pk'],))
urls.py
path("trip_details/<int:pk>/new_catch/", views.NewCatchView.as_view(), name="new_catch"),
Thank you in advance for your help!
You're almost there. You've created the kwarg, so now you just need to use it in the form to overwrite the original queryset:
class CatchForm(forms.ModelForm):
...
def __init__(self, *args, **kwargs):
fisherman = kwargs.pop('fisherman')
super().__init__(*args, **kwargs)
self.fields['fisherman'].queryset = fisherman

django access foreignkey value on CreateView class

I have a form that submits data, but before submitting to the database it will take the value from foreign first.
here my models.py
class Employee(models.Model):
nik = models.CharField(max_length=100)
name = models.CharField(max_length=100)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse("system:detail",kwargs={'pk':self.pk})
class EmpLoan(models.Model):
status = models.BooleanField()
nominal = models.DecimalField(max_digits=10, decimal_places=0)
emp = models.ForeignKey(Employee, related_name='emploan')
created_at = models.DateTimeField(auto_now=True)
updated_at = models.DateTimeField(auto_now_add=True, null=True)
def __str__(self):
return self.emp.name
class EmpInstallment(models.Model):
nominal = models.DecimalField(max_digits=10, decimal_places=0)
loan = models.ForeignKey(EmpLoan, related_name='empinstallment')
created_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.loan.emp.name
view.py
class EmployeeCreateView(CreateView):
fields = ()
model = models.Employee
def form_valid(self, form):
self.object = form.save(commit=False)
data = self.object.EmpLoan.EmpInstallment.object.get(id=1)
self.object.nik = data
self.object.save()
return super(ModelFormMixin, self).form_valid(form)
how to access data on EmpInstallment?...
iam trying use this data = self.object.EmpLoan.EmpInstallment.object.get(id=1)
but it's not working.
iam still learner and very noob on django
EmpInstallment -- > ForeignKey to ---> EmpLoan -----> ForeignKey to ---Employee
class EmployeeCreateView(CreateView):
fields = ()
model = models.Employee
def form_valid(self, form):
employee_instance = form.save(commit=False)
# try to get the emploan instance
emploan = employee_instance.emploan.first()
if emploan:
empinstallment = emploan.empinstallment.first()
if empinstallment:
pass # there you can execute your codes
# there you have access to empinstallment

Cannot assign must be a instance Django

I have an order form which returns this statement of submit:
Cannot assign "<Annual: 2012>": "Order.annuals" must be a "Catalog" instance.
I'm fairly new to Django. I understand it needs an instance instead of the string it has been passed. How would I go about resolving that?
Here is my view:
class OrderListCreateView(
views.LoginRequiredMixin,
views.SetHeadlineMixin,
generic.CreateView
):
form_class = forms.OrderListForm
headline = 'Create'
model = Order
template_name = 'ordercreate.html'
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.user = self.request.user
self.object.save()
return super(OrderListCreateView, self).form_valid(form)
Here is my form:
class OrderListForm(forms.ModelForm):
annuals = forms.ModelChoiceField(queryset=Annual.objects.all())
issues = forms.ModelChoiceField(queryset=Issue.objects.all())
articles = forms.ModelChoiceField(queryset=Article.objects.all())
class Meta:
fields = (
'annuals',
'issues',
'articles',)
model = models.Order
def __init__(self, *args, **kwargs):
super(OrderListForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.layout = Layout(
'annuals',
'issues',
'articles',
ButtonHolder(
Submit('create', 'Create')
)
)
Here is my model:
class Catalog(models.Model):
products = models.CharField(max_length=200)
def __unicode__(self):
return self.products
class Issue(models.Model):
catalog = models.ForeignKey(Catalog, related_name='issue_products')
Volume = models.DecimalField(max_digits=3, decimal_places=1)
def __unicode__(self):
return unicode(self.Volume)
class Annual(models.Model):
catalog = models.ForeignKey(Catalog, related_name='annual_products')
year_id = models.IntegerField(max_length=4)
start_date = models.CharField(max_length=6)
end_date = models.CharField(max_length=6)
def __unicode__(self):
return unicode(self.year_id)
#def __unicode__(self):
# return unicode(self.id)
class Annual_Issue(models.Model):
annual_id = models.ForeignKey(Annual, related_name='annual_ids')
issue_id = models.ForeignKey(Issue, related_name='issues')
def __unicode__(self):
return self.annual_id
class Article(models.Model):
catalog = models.ForeignKey(Catalog, related_name='article_products')
title = models.CharField(max_length=200)
abstract = models.TextField(max_length=1000, blank=True)
full_text = models.TextField(blank=True)
proquest_link = models.CharField(max_length=200, blank=True, null=True)
ebsco_link = models.CharField(max_length=200, blank=True, null=True)
def __unicode__(self):
return self.title
class Order(models.Model):
user = models.ForeignKey(User, related_name='who_ordered')
annuals = models.ForeignKey(Catalog, related_name='annuals_ordered', blank=True, null=True)
issues = models.ForeignKey(Catalog, related_name='issues_ordered', blank=True, null=True)
articles = models.ForeignKey(Catalog, related_name='items_ordered', blank=True, null=True)
In your Order model, you have defined a ForeignKey relationship for several other models (Annual, Issue, and Article), but each of these relationships points to the Catalog model. When you attempt to save the Order instance created by your form, it has received objects of these types (Annual, Issue, and Article), but it cannot store a foreign-key reference to these objects in the fields defined on the Order model. This is due to the foreign-key fields on the Order demanding that they can only contain a reference to Catalog objects.
If, for each of these foreign-key relationships, you wish to store one of these various kinds of objects, you will need to alter your Order model definition to expect references to objects of those models rather than Catalog objects.
In brief, I would suggest that the Order model be modified to include the following relationships. This will allow an order object to store a single reference to an object of each other kind (Annual, Issue, and Article).
annuals = models.ForeignKey(Annual, related_name='annuals_ordered', blank=True, null=True)
issues = models.ForeignKey(Issue, related_name='issues_ordered', blank=True, null=True)
articles = models.ForeignKey(Article, related_name='items_ordered', blank=True, null=True)
For more information about ForeignKey relationships in Django, see the reference here.