Django ModelForm with ForeignKey - python

I have 2 models, Item and OnHand. Item represents the product, like a USB Keyboard, while OnHand represents an actual USB Keyboard.
class Item(models.Model):
name = models.CharField(max_length=100)
description = models.CharField(max_length=100)
manufacturer = models.ForeignKey(Manufacturer, blank=True, null=True, on_delete=models.SET_NULL)
category = models.ForeignKey(Category, blank=True, null=True, on_delete=models.SET_NULL)
vendor = models.ForeignKey(Vendor, blank=True, null=True, on_delete=models.SET_NULL)
vendor_sku = models.CharField(max_length=80, default='000000000')
replenish_threshold = models.IntegerField(max_length=1000, default=10)
max_onhand = models.IntegerField(max_length=10000, default=30)
introduction = models.DateField(auto_now=True)
tags = models.ManyToManyField(Tag)
slug = models.SlugField(max_length=100, default=slugify(name))
class Meta:
verbose_name_plural = 'items'
def __str__(self):
return self.name
class OnHand(models.Model):
name = models.CharField(max_length=100)
serial = models.CharField(max_length=80, default='Consumable')
asset = models.CharField(max_length=20, null=True, blank=True)
product = models.ForeignKey(Item, blank=True, null=True, on_delete=models.CASCADE)
tags = models.ManyToManyField(Tag)
class Meta:
verbose_name_plural = 'items on hand'
def __str__(self):
if not self.serial:
return self.name
return self.serial
I have a ModelForm I use for adding new Items and OnHand, the form I'm working with is at /inventory/add_onhand// so /inventory/add_onhand/1/ would be the form to add a keyboard to inventory for Item with a primary key of 1.
class OnHandForm(forms.ModelForm):
class Meta:
model = OnHand
fields = {'name', 'asset', 'serial',}
def add_onhand(request, query):
if request.method == "POST":
form = OnHandForm(request.POST)
if form.is_valid():
form.save()
return redirect('view_item')
else:
print(query)
form = OnHandForm()
item = Item.objects.get(pk=query)
return render(request, 'add_onhand.html', {'form':form, 'item':item})
When saving a new OnHand via this form, how would I direct the relationship to Item?

I am still hoping for a better solution, but in the meantime what I've done was added product to the form, but using JQuery to hide the element and automatically select it based on the Item's primary key.
This is hacky, but it works. I'd hope for a more elegant solution.

Related

How can I implement the same widget that Django uses to ManyToMany fields in the admin page?

My models:
class Ingredient(models.Model):
BASE_UNIT_CHOICES = [("g", "Grams"), ("ml", "Mililiters")]
CURRENCY_CHOICES = [("USD", "US Dollars"), ("EUR", "Euro")]
ingredient_id = models.AutoField(primary_key=True)
name = models.CharField(max_length=200)
base_unit = models.CharField(max_length=2, choices=BASE_UNIT_CHOICES)
cost_per_base_unit = models.FloatField()
currency = models.CharField(
max_length=3, choices=CURRENCY_CHOICES, default="EUR")
def __str__(self):
return self.name
class RecipeIngredient(models.Model):
quantity = models.FloatField()
ingredient_id = models.ForeignKey(Ingredient, on_delete=models.CASCADE)
def __str__(self):
return f"{self.quantity} / {self.ingredient_id}"
class Recipe(models.Model):
recipe_id = models.AutoField(primary_key=True)
name = models.CharField(max_length=200)
ingredients = models.ManyToManyField(RecipeIngredient)
date_created = models.DateTimeField('Date Created')
def __str__(self):
return f"{self.name}, {self.ingredients}"
When I use the admin page, it has this + button that allows me to create new ingredient/quantity combinations
like this
But when I try to use it from a form in my code it looks like
this
Here is my form code:
class AddRecipeForm(forms.ModelForm):
class Meta:
model = Recipe
fields = ['name', 'ingredients', 'date_created']
You should write the 'widgets' for each field in you Form that need configuration.
Check the documentation 'Widgets in forms', or even, you can define your own Widgets.

Django: Limit choices in a models.Charfield by PrimaryKey

Another question from me tonight and I hope I can explain it adequately:
I got three classes in my "models.py":
class Customer(models.Model):
full_name = models.CharField(max_length=100, null=True, unique=True)
short_name = models.CharField(max_length=8, null=True, unique=True)
class Project(models.Model):
customer = models.ForeignKey(Customer, null=False, on_delete=models.CASCADE)
name = models.CharField(max_length=255, null=True, unique=True)
...
class Entry(models.Model):
user = models.ForeignKey(User, null=True, blank=False, on_delete=models.CASCADE)
customer = models.ForeignKey(Customer, null=True, blank=False, on_delete=models.CASCADE)
project = models.ForeignKey(Project, null=True, blank=False, on_delete=models.CASCADE)
date = models.DateField()
shortText = models.CharField(max_length=100, null=False, blank=False)
...
Note: One Customer can have multiple Projects.
On one of my sites there's a table with buttons beside each "Customer". The plan is, that it should lead me to another page, were the user can write and save his "Entry". Right now, the PrimaryKey inside the Button/Link contains the ID of the "Customer".
My question is: is it possible to limit the choices of the "Project" (inside a Drop-Down-Menu) to the "Customer" that has been clicked on? And is creating a ModelForm the right thing to do?
Thanks to all of you and a good night!
Well, don't know if this is the right way, but I found a solution for my problem:
Wrote a "forms.ModelForm" for my view-function...
class EntryForm(ModelForm):
class Meta:
model = Entry
fields = '__all__'
def __init__(self, *args, pk, **kwargs):
super().__init__(*args, **kwargs)
self.fields['project'].queryset = Project.objects.filter(customer_id=pk)
Insert my ModelForm into the view-function...
def WriteEntry(request, pk):
form = EntryForm(pk=pk)
if request.method =='POST':
form = EntryForm(request.POST, pk)
if form.is_valid():
form.save()
...
context = {'form': form}
return render(request, '...html', context)

One To Many and models

i am trying to build my first project, a CRM website to handle orders and inventory.
and i got stuck, i was able to link orders to customer.
but when i try to build order that contain multi items. for some reason i didn't find a way to do it.
hope you can assist me.
so I have User>>Order>>Multi Items.
questions:
1) does the best practice here is just use ForeignKey ?
this my model's code:
from django.db import models
class Customer(models.Model):
name = models.CharField(max_length=200, null=True)
phone = models.CharField(max_length=200, null=True)
email = models.CharField(max_length=200, null=True)
date_created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
def date_createdview(self):
return self.date_created.strftime('%B %d %Y')
class Product(models.Model):
CATEGORY = (
('General', 'General'),
('SmartHome', 'SmartHome'),
('Software', 'Software'),
('Mobile', 'Mobile'),
)
name = models.CharField(verbose_name='שם', max_length=200, null=True)
price = models.FloatField(verbose_name='מחיר', null=True)
category = models.CharField(max_length=200, null=True, choices=CATEGORY, verbose_name='קטגוריה')
description = models.CharField(max_length=200, null=True, verbose_name='מלל חופשי')
date_created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
class Order(models.Model):
STATUS = (
('New', 'New'),
('Work in progress', 'Work in progress'),
('completed', 'completed'),
)
customer = models.ForeignKey(Customer, null=True, on_delete=models.CASCADE)
date_created = models.DateTimeField(auto_now_add=True, null=True)
status = models.CharField(max_length=200, null=True, choices=STATUS)
def date_createdview(self):
return self.date_created.strftime('%d/%m/%Y')
class OrderItem(models.Model):
product = models.ForeignKey(Product, null=True, on_delete=models.CASCADE)
order = models.ForeignKey(Order, null=True, on_delete=models.CASCADE)
quantity = models.IntegerField(null=True)
2)how should I build my views or forms?
i want to make it dynamic, when i enter the order i can insert items and see the new item get add to a list of the items in the order.
how can save the order number and add new items?
this is my product view, it's working. I can add new products.
#login_required(login_url="login")
def products(request):
form = ProductForm(request.POST or None)
if form.is_valid():
form.save()
products = Product.objects.all()
context = {'form': form, 'products': products}
return render(request, 'accounts/products.html', context)
hope you can direct me to the right place.
thank you!
if form.is_valid():
order = get_object_or_404(Order, id=id)
instance = form.save(commit=False)
instance.order=order
instance.save()
just need to do:
commit=False
and then link the order.

Save a many to many object inside a django model?

I am still learning to use Django and so I am a bit unclear on something.
I have a product model and category model. A product can lie in multiple categories and multiple categories can have the same product.
So, its a many to many relationship. Now, I want to allow the user to select multiple categories from the html and then I want to save the categories and link them to the category object in my product model. I am completely lost about it.
One way would be to use Modelform but I dont want to go that way. Is there any other way I can accomplish this?
models.py:
class Category(models.Model):
name = models.CharField(max_length=50)
slug = models.SlugField(max_length=50, unique=True,
help_text='Unique value for product page URL, created from name.')
description = models.TextField()
is_active = models.BooleanField(default=True)
meta_keywords = models.CharField("Meta Keywords", max_length=255,
help_text='Comma-delimited set of SEO keywords for meta tag')
meta_description = models.CharField("Meta Description", max_length=255,
help_text='Content for description meta tag')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('catalog:categories', kwargs={'category_slug': self.slug})
class Meta:
ordering = ['-created_at']
verbose_name_plural = 'Categories'
class Product(models.Model):
name = models.CharField(max_length=255, unique=True)
slug = models.SlugField(max_length=255, unique=True,
help_text='Unique value for product page URL, created from name.')
brand = models.CharField(max_length=50)
sku = models.CharField(max_length=50)
price = models.DecimalField(max_digits=9, decimal_places=2)
old_price = models.DecimalField(max_digits=9, decimal_places=2, blank=True, default=0.00)
thumbnail = models.FileField(upload_to='static/images/products/thumbnails')
imageurls = models.CharField(max_length=1000)
is_active = models.BooleanField(default=True)
is_bestseller = models.BooleanField(default=False)
is_featured = models.BooleanField(default=False)
quantity = models.IntegerField()
description = models.TextField()
meta_keywords = models.CharField(max_length=255, help_text='Comma-delimited set of SEO keywords for meta tag')
meta_description = models.CharField(max_length=255, help_text='Content for description meta tag')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
categories = models.ManyToManyField(Category)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('catalog:products', kwargs={'product_slug': self.slug})
def sale_price(self):
if self.old_price > self.price:
return self.price
else:
return None
class Meta:
ordering = ['-created_at']
part of views.py:
if request.method =='POST':
print ('entered')
name = request.POST['name']
brand = request.POST['brand']
sku = request.POST['sku']
price = request.POST['price']
quantity = request.POST['quantity']
description = request.POST['description']
imageurls = request.POST['urls']
print('imageurls',imageurls)
categorylist = request.POST['categories']
print('categorylist',categorylist)
categories = re.findall(r"[\w']+", categorylist)
print categories
imageurls = imageurls.split('~')
print('iageurls',imageurls)
for x in categories:
categoryobj = Category.objects.filter(name=x).values()
print ('categoryobj',categoryobj)
# Product.objects.create(name=name,sku=sku,brand=brand,price=price,quantity=quantity,description=description,imageurls=imageurls,categories=categoryobj)
return HttpResponse('success')
Try to save the above way gives me error.
product=Product.objects.create(name=name,sku=sku,brand=brand,price=price,quantity=quantity,description=description,imageurls=imageurls)
category_queryset = []
for x in categories:
category = Category.objects.filter(name=x).first()
category_queryset.append(category)
product.categories.set(category_queryset)

Testing Django Inline ModelForms: How to arrange POST data?

I have a Django 'add business' view which adds a new business with an inline 'business_contact' form.
The form works fine, but I'm wondering how to write up the unit test - specifically, the 'postdata' to send to self.client.post(settings.BUSINESS_ADD_URL, postdata)
I've inspected the fields in my browser and tried adding post data with corresponding names, but I still get a 'ManagementForm data is missing or has been tampered with' error when run.
Anyone know of any resources for figuring out how to post inline data?
Relevant models, views & forms below if it helps. Lotsa thanks.
MODEL:
class Contact(models.Model):
""" Contact details for the representatives of each business """
first_name = models.CharField(max_length=200)
surname = models.CharField(max_length=200)
business = models.ForeignKey('Business')
slug = models.SlugField(max_length=150, unique=True, help_text=settings.SLUG_HELPER_TEXT)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
phone = models.CharField(max_length=100, null=True, blank=True)
mobile_phone = models.CharField(max_length=100, null=True, blank=True)
email = models.EmailField(null=True)
deleted = models.BooleanField(default=False)
class Meta:
db_table='business_contact'
def __unicode__(self):
return '%s %s' % (self.first_name, self.surname)
#models.permalink
def get_absolute_url(self):
return('business_contact', (), {'contact_slug': self.slug })
class Business(models.Model):
""" The business clients who you are selling products/services to """
business = models.CharField(max_length=255, unique=True)
slug = models.SlugField(max_length=100, unique=True, help_text=settings.SLUG_HELPER_TEXT)
description = models.TextField(null=True, blank=True)
primary_contact = models.ForeignKey('Contact', null=True, blank=True, related_name='primary_contact')
business_type = models.ForeignKey('BusinessType')
deleted = models.BooleanField(default=False)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
address_1 = models.CharField(max_length=255, null=True, blank=True)
address_2 = models.CharField(max_length=255, null=True, blank=True)
suburb = models.CharField(max_length=255, null=True, blank=True)
city = models.CharField(max_length=255, null=True, blank=True)
state = models.CharField(max_length=255, null=True, blank=True)
country = models.CharField(max_length=255, null=True, blank=True)
phone = models.CharField(max_length=40, null=True, blank=True)
website = models.URLField(null=True, blank=True)
class Meta:
db_table = 'business'
def __unicode__(self):
return self.business
def get_absolute_url(self):
return '%s%s/' % (settings.BUSINESS_URL, self.slug)
VIEWS:
def business_add(request):
template_name = 'business/business_add.html'
if request.method == 'POST':
form = AddBusinessForm(request.POST)
if form.is_valid():
business = form.save(commit=False)
contact_formset = AddBusinessFormSet(request.POST, instance=business)
if contact_formset.is_valid():
business.save()
contact_formset.save()
contact = Contact.objects.get(id=business.id)
business.primary_contact = contact
business.save()
#return HttpResponse(help(contact))
#business.primary = contact.id
return HttpResponseRedirect(settings.BUSINESS_URL)
else:
contact_formset = AddBusinessFormSet(request.POST)
else:
form = AddBusinessForm()
contact_formset = AddBusinessFormSet(instance=Business())
return render_to_response(
template_name,
{
'form': form,
'contact_formset': contact_formset,
},
context_instance=RequestContext(request)
)
FORMS:
class AddBusinessForm(ModelForm):
class Meta:
model = Business
exclude = ['deleted','primary_contact',]
class ContactForm(ModelForm):
class Meta:
model = Contact
exclude = ['deleted',]
AddBusinessFormSet = inlineformset_factory(Business,
Contact,
can_delete=False,
extra=1,
form=AddBusinessForm,
)
The problem is you have not included the management form in your data. You need to include form-TOTAL_FORMS (total number of forms in the formset, default is 2), form-INITIAL_FORMS (the initial number of forms in the formset, default is 0) and form-MAX_NUM_FORMS (the maximum number of forms in the formset, default is '').
See the Formset documentation for more information on the management form.

Categories