Django forms target foreign key - django

I have a form for submitting an order. Multiple items have been attached to a catalog object, I'd like to have the form dropdown contain options for all of the items attached to the foreign key, instead of the foreign key Catalog name of Available. I know how to access these in the view, using the related name, is this possible in forms?
Here is my current form:
from django import forms
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, ButtonHolder, Submit
from . import models
class OrderListForm(forms.ModelForm):
class Meta:
fields = ('order_lines',)
model = models.Order
def __init__(self, *args, **kwargs):
super(OrderListForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.layout = Layout(
'order_lines',
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.catalog)
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)
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')
order_lines = models.ForeignKey(Issue, related_name='items_ordered')

you can access all the Annuals and Articles that are in the same catalog by using:
c = Catalog.objects.get(....
c.article_products_set.all()
c.annual_products_set.all()

Related

Django foreignkey between two databases

I'm looking to connect two SQLite tables together (oh dear).
I found this solution : How to use django models with foreign keys in different DBs? , I adapted it to my models and my code (I think). I have no bad answers from django. However, I would like to modify the entry with the admin view of django, and when I try to add the entry with the foreign key of another database, I get this answer:
Exception Type: OperationalError at /admin/users/character/add/
Exception Value: no such table: books
How to adapt to fix it?
models database default
from django.db import models
from users.related import SpanningForeignKey
from books.models import Books
class Character(models.Model):
last_name = models.fields.CharField(max_length=100)
first_name = models.fields.CharField(max_length=100)
book = SpanningForeignKey('books.Books', null=True, on_delete=models.SET_NULL)
def __str__(self):
return f'{self.first_name} {self.last_name}'
models external database
from django.db import models
# Create your models here.
class Books(models.Model):
title = models.TextField()
sort = models.TextField(blank=True, null=True)
timestamp = models.TextField(blank=True, null=True) # This field type is a guess.
pubdate = models.TextField(blank=True, null=True) # This field type is a guess.
series_index = models.FloatField()
author_sort = models.TextField(blank=True, null=True)
isbn = models.TextField(blank=True, null=True)
lccn = models.TextField(blank=True, null=True)
path = models.TextField()
flags = models.IntegerField()
uuid = models.TextField(blank=True, null=True)
has_cover = models.BooleanField(blank=True, null=True)
last_modified = models.TextField() # This field type is a guess.
class Meta:
managed = False
db_table = 'books'
app_label = 'books'
class Languages(models.Model):
lang_code = models.TextField()
class Meta:
managed = False
db_table = 'languages'
app_label = 'books'
class BooksLanguagesLink(models.Model):
book = models.ForeignKey(Books, null=True, on_delete=models.SET_NULL, db_column='book')
lang_code = models.ForeignKey(Languages, null=True, on_delete=models.SET_NULL, db_column='lang_code')
item_order = models.IntegerField()
class Meta:
managed = False
db_table = 'books_languages_link'
app_label = 'books'
admin books
from django.contrib import admin
from books.models import Books, Languages
class BooksModelAdmin(admin.ModelAdmin):
using = 'calibre_db'
def save_model(self, request, obj, form, change):
obj.save(using=self.using)
def delete_model(self, request, obj):
obj.delete(using=self.using)
def get_queryset(self, request):
return super().get_queryset(request).using(self.using)
def formfield_for_foreignkey(self, db_field, request, **kwargs):
return super().formfield_for_foreignkey(db_field, request, using=self.using, **kwargs)
def formfield_for_manytomany(self, db_field, request, **kwargs):
return super().formfield_for_manytomany(db_field, request, using=self.using, **kwargs)
# Register your models here.
admin.site.register(Books, BooksModelAdmin)

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)

Filter options in one to one field django admin

I have this 2 models
from django.db import models
def get_upload_path(instance, filename):
return '{0}/{1}'.format(instance.imovel.id, filename)
# Create your models here.
class Imovel(models.Model):
nome = models.CharField(max_length=50)
descricao = models.CharField(max_length=800)
area = models.IntegerField()
quartos = models.SmallIntegerField(null=True, blank=True)
banheiros = models.SmallIntegerField()
disponivel_aluguel = models.BooleanField()
disponivel_venda = models.BooleanField()
valor_aluguel = models.DecimalField(max_digits=15, decimal_places=2)
valor_venda = models.DecimalField(max_digits=15, decimal_places=2)
valor_condominio = models.DecimalField(max_digits=15, decimal_places=2)
valor_iptu = models.DecimalField(max_digits=15, decimal_places=2)
capa = models.OneToOneField('ImagemImovel', related_name='capa', on_delete=models.DO_NOTHING, null=True, blank=True)
def __str__(self):
return self.nome
class Meta:
db_table = 'imovel'
class ImagemImovel(models.Model):
imovel = models.ForeignKey(Imovel, related_name='imagens', on_delete=models.CASCADE)
nomeImagem = models.CharField(max_length=20)
imagem = models.ImageField(upload_to=get_upload_path)
def __str__(self):
return self.nomeImagem
class Meta:
db_table = 'imagemImovel'
I have houses and it's pictures and a field named 'capa' to say which one is the main picture.
The problem is when I add a house through django admin, save and come back to choose the main one it let me choose images from other houses. How could I filter it to just show images that are related to this specific house?
My django admin file
from django.contrib import admin
from .models import ImagemImovel, Imovel
# Register your models here.
class ImagemImovelAdmin(admin.TabularInline):
model = ImagemImovel
class ImovelAdmin(admin.ModelAdmin):
inlines = (ImagemImovelAdmin, )
admin.site.register(Imovel, ImovelAdmin)
I changed my ImovelAdmin class so I can get the id from the model using the get_form function and use it in a filter for the queryset
class ImovelAdmin(admin.ModelAdmin):
inlines = (ImagemImovelAdmin, )
id_for_formfield = None
def get_form(self, request, obj=None, **kwargs):
if obj:
self.id_for_formfield = obj.id
return super(ImovelAdmin, self).get_form(request, obj, **kwargs)
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == "capa" and self.id_for_formfield is not None:
kwargs["queryset"] = ImagemImovel.objects.filter(imovel=self.id_for_formfield)
return super(ImovelAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
admin.site.register(Imovel, ImovelAdmin)

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.

Django Crispy Forms show related values of foreign key

I have an order form which displays available items which are attached to a Catalog object. Instead of displaying the available items, it is currently only displaying the field name of available instead of the the items available. Is there a way to target the values in the crispy form? Similar to how it is done in the template like:
{% for i in catalog.annual_products.all %}
Here is my form:
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, ButtonHolder, Submit
from . import models
class OrderListForm(forms.ModelForm):
class Meta:
fields = ('order_lines',)
model = models.Order
def __init__(self, *args, **kwargs):
super(OrderListForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.layout = Layout(
'order_lines',
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.catalog)
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)
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')
order_lines = models.ForeignKey(Catalog, related_name='items_ordered')
This is what I'm currently returning in the template: (which is just the name of the products field in Catalog)