Django - Get Related Key and Insert into Database - python

Ok, so what I'm trying to do is allow the user to add a "product" to their shop but without having to choose the shop to add it to as each user will only have ONE shop.
I'm getting the:
"IntegrityError at /shop/product/add/
NOT NULL constraint failed: shop_product.business_id"
This is what's being shown in the local variables:
Local Vars
Local Vars:
Variable Value
__class__ <class 'shop.views.ProductCreate'>
form <AddProductForm bound=True, valid=True, fields=(product_name;product_desc;product_image)>
s <Shop: 4>
self <shop.views.ProductCreate object at 0x048B0370>
user 10
Now I believe the issue might be the "s" variable's as the code is actually getting the correct shop.. but it's also adding that weird "
My Code as it is right now.
models.py
# Shop Model. A Shop Object will be created when the user registers
class Shop(models.Model):
name = models.CharField(max_length=150)
owner = models.OneToOneField(User, related_name="owner")
shop_logo = models.FileField()
def __str__(self):
return str(self.name) + ": " + str(self.owner)
def create_shop(sender, **kwargs):
user = kwargs["instance"]
if kwargs["created"]:
up = Shop(owner=user)
up.save()
post_save.connect(create_shop, sender=User)
def shoplogo_or_default(self, default_path='/static/images/dft/no-img.png'):
if self.shop_logo:
return self.shop_logo
return default_path
# The class that will link a product to the shop
class Product(models.Model):
product_name = models.CharField(max_length=250)
# connect the product to the shop
business = models.ForeignKey(Shop, on_delete=models.CASCADE, related_name="products")
product_desc = models.TextField()
product_image = models.FileField()
def __str__(self):
return self.product_name
views.py
class ProductCreate(CreateView):
model = Product
form_class = AddProductForm
template_name = 'shop/add-product.html'
def form_valid(self, form):
form.save(commit=False)
# get current logged in user
user = self.request.user.id
# match the current logged in user to an owner in the Shop model
s = Shop.objects.get(owner=user)
# get the id of that owner's shop identification number
form.business = str(s.id)
form.save()
# This method is called when valid form data has been POSTed.
# It should return an HttpResponse.
return super(ProductCreate, self).form_valid(form)
The above should in theory get the current logged in user, match that user to a shop within the shop model as an owner and then get that shop ID.
forms.py
class AddProductForm(forms.ModelForm):
class Meta:
model = Product
fields = ['product_name', 'product_desc', 'product_image']
exclude = ['business']
I'm rather new to Django and a student so I'd like to apologise if you see anything weird.
Thank you :)

You're close, but don't try to edit the shop value into the form. Instead, capture the in-memory Product instance from saving the form and assign its business attribute:
def form_valid(self, form):
new_product = form.save(commit=False)
# get current logged in user
user = self.request.user.id
# match the current logged in user to an owner in the Shop model
s = Shop.objects.get(owner=user)
# assign the shop instance to the product
new_product.business = s
# record the product to the database
new_product.save()
# This method is called when valid form data has been POSTed.
# It should return an HttpResponse.
return super(ProductCreate, self).form_valid(form)

Related

How can I create new 'draft' object with class based UpdateView before form filling?

I have a product model that has a lot of field. When user click to "Create New Product" I want create new object (just create object that it has id, seller, draft status), and show blank form. Because I want to use Ajax asyncronous upload to this object. User fills the object's other fields (like upload images) and save. Also user should be able to edit own draft products on own dashboard.
First problem: I used UpdateView, but If I override get_object method and I click "Create New Product", django calls twice this method and create two object?! I want call just one time.
Second problem: When users wants to edit draft items, they go to dashboard and edit someone. Should I create new class-based view for this or can I use same view for both operation? Maybe I can add get_or_create method to my get_object method, is It possible?
my model:
class Product(models.Model):
seller = models.ForeignKey("auth.User", on_delete=models.CASCADE, verbose_name="Seller", default=None)
category = models.ForeignKey("Category", on_delete=models.CASCADE, verbose_name="Category Name", blank=True, null=True)
title = models.CharField(max_length=50, verbose_name="Product Title", blank=True, null=True)
status = models.IntegerField(choices=((x.value, x.name.title()) for x in Status), default=Status.TASK)
images = models.ManyToManyField("ProductImages", blank=True, null=True)
my class-based view:
class ProductCreateViewTest(LoginRequiredMixin, UpdateView):
template_name = 'product/add.html'
form_class = ProductForm
model = Product
success_url = get_success_url
def get_object(self, queryset=None):
obj = Product.objects.create(seller=self.request.user, status=Status.TASK.value)
print("new obj created:{}".format(obj))
return obj
def form_valid(self, form):
product = form.save(commit=False)
product.seller = self.request.user
product.comission_rate = Comission.objects.get(category=product.category)
product.save()
self.success_url = Product.get_absolute_url(product)
return super(ProductCreateViewTest, self).form_valid(form)
I solved it!
Firstly I create new object with my small function and redirect to UpdateView like this:
def redirect_to_process(request):
product = Product.objects.create(seller=request.user)
form = ProductForm(request.POST or None, request.FILES or None, instance=product)
print("new product:{}".format(product))
return HttpResponseRedirect("/products/process/{}".format(product.id))
Secondly I display blank form (actually not blank, it has object id ;) ) with UpdateView like this:
class ProductCreateViewTest(LoginRequiredMixin, UpdateView):
template_name = 'product/add.html'
form_class = ProductForm
model = Product
def form_valid(self, form):
product = self.object
product = form.save(commit=False)
if not product.slug:
product.slug = product.get_slug()
comission_rate = Comission.objects.get(category=product.category)
product.income = float(product.price) * (1 -float(comission_rate.rate))
product.comission_rate = Comission.objects.get(category=product.category)
product.save()
self.success_url = Product.get_absolute_url(product)
return super(ProductCreateViewTest, self).form_valid(form)
If my user want to edit this product, I redirect to same view (UpdateView) again.
So I don't need two class-based view! Write less code and get more done.

How to change a field in parent class while creating chiled class fields in forms.py and views.py?

I'm was creating ModelForm I try to make change the parent class while saving child class fields to the database, in the views.py I made but it didn't save to the database.
here is my model.py
class Table(models.Model):
restaurant = models.ForeignKey(Restaurant, on_delete=models.CASCADE)
book = models.BooleanField(default=False)
class People(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, null=True)
taple = models.OneToOneField(Table, on_delete=models.CASCADE, null=True, blank=True)
#receiver(post_save, sender=User)
def update_people_profile(sender, instance, created, **kwargs):
try:
instance.people.save()
except ObjectDoesNotExist:
People.objects.create(user=instance)
Class People is the child class and Table is the parent class so I'm using People class for making forms. here is my forms.py
class Booking(forms.ModelForm):
class Meta:
model = People
fields = [
'taple',
]
So I want to make True book field in Table class and save it to the database when saving Booking form. here is my views.py
def booking(request):
if request.method == 'POST':
try:
people_instance = People.objects.get(user=request.user)
except Table.DoesNotExist:
people_instance = People(user=request.user)
form = Booking(request.POST, instance=people_instance)
if form.is_valid():
user = form.save(commit=False)
user.taple.booking = True
user.refresh_from_db()
user.user = request.user
user.taple = form.cleaned_data.get('taple')
user.save()
print(user.taple.booking, user.taple.id)
return redirect('booked')
else:
form = Booking()
return render(request, 'main/booking.html', {'form': form})
Any Idea?
What I understand from the snippets is that you want to be able to record if a table is booked (book Boolean Field in your Table model and if so by whom, which is the object of your People model.
If my understanding is correct, then I don't think you really need a join table (People model). Instead, I would change your model as follow:
class Table(models.Model):
restaurant = models.ForeignKey(Restaurant, on_delete=models.CASCADE)
booked_by = models.OneToOneField(User, null=True, on_delete=models.SET_NULL, related_name='table_booked')
#property
def is_booked(self):
# This returns True if booked_by is set, False otherwise
return self.booked_by_id is not None
This way you don't need the People model. The property decorator will allow you to use is_booked as a calculated field.
Also, note the related name which will be used in the form:
class BookingForm(forms.ModelForm):
table_booked = forms.ModelChoiceField(queryset=Table.objects.filter(booked_by__isnull=True))
class Meta:
model = User
fields = ['table_booked',]
In the form, you will see that we define a custom queryset for table_booked. THe aim is to filter for free tables only.
Then you can hopefully simplify as well your view as follow:
Update:
As table_booked is a reverse foreign key, we actually need to save the table object which contains the relation. Here is the modified view:
#login_required
def booking(request):
form = BookingForm(request.POST or None, instance=request.user)
if form.is_valid():
user = form.save(commit=False)
tbl = form.cleaned_data['table_booked']
tbl.booked_by = request.user
tbl.save()
user.save()
print(request.user.table_booked.id, request.user.table_booked.is_booked)
return redirect('/')
return render(request, 'booking/booking.html', {'form': form})
Note: I haven't tested the code so there could be some typos but that should help you getting started.

UNIQUE constraint failed: rango_category.name

Hi im following the tango with django tutorial.. I've searched for a solution to this but nothing!
the error:
IntegrityError at /rango/add_category/
UNIQUE constraint failed: rango_category.name
my model:
from django.db import models
# Create your models here.
class Category(models.Model):
name = models.CharField(max_length=128, unique=True)
views = models.IntegerField(default=0)
likes = models.IntegerField(default=0)
def __unicode__(self):
return self.name
class Page(models.Model):
category = models.ForeignKey(Category) #ForeignKey denotes a relationship between page and category
title = models.CharField(max_length=128)
url = models.URLField()
views = models.IntegerField(default=0)
def __unicode__(self):
return self.title
my add_category view:
def add_category(request):
# Get the context from the request.
context = RequestContext(request)
# A HTTP POST?
if request.method == 'POST':
form = CategoryForm(request.POST)
#Have we been provided with a valid form?
if form.is_valid():
#save the new category to the database
form.save(commit=True)
# Now call the index() view.
# The user will be shown the Homepage.
return index(request)
else:
# The supplied form contained errors - just print them to the terminal
print (form.errors)
else:
form = CategoryForm()
# Bad form (or form details), no form supplied...
# Render the form with error message(if any).
return render_to_response('rango/add_category.html', {'form':form}, context)
my forms:
from django import forms
from rango.models import Page, Category
class CategoryForm(forms.ModelForm):
names = forms.CharField(max_length=128, help_text="please enter the category name.")
views = forms.IntegerField(widget=forms.HiddenInput(), initial=0)
likes = forms.IntegerField(widget=forms.HiddenInput(), initial=0)
#an inline class to to provide additional information on the form
class Meta:
# provide an association between the Modelform and a model
model = Category
fields = ('views', 'likes')
class PageForm(forms.ModelForm):
title = forms.CharField(max_length=128, help_text="Please enter the title of the page")
url = forms.URLField(max_length=200, help_text="Please enter the url of the page")
views = forms.IntegerField(widget=forms.HiddenInput(),initial=0)
class Meta:
# Provide an association between the ModelForm and a model
model = Page
#what fields do we want to include in our form
# this way we dont need every field in the model present
# Some fields may allow NULL values, so we may not want to include them...
# Here we are hiding.
fields = ('title', 'url', 'views')
'name' field is missing in CategoryForm's Meta 'fields'. Since Category::name is a unique field and default is not possible, any attempt to save will fail.
If the model does not allow the missing fields to be empty, and does
not provide a default value (not possible for unique) for the missing fields, any attempt to save() a ModelForm with missing fields will fail.

Saving django OneToOneField current user with modelform

I have spent a good few hours looking over the documentation and on here as well and i still can't find an answer to my issue. please if you know of one direct me to it. otherwise please look at the following issue. I receive a KeyError when trying to register a user as a host for an open source homestay project im working on: https://github.com/castaway2000/OpenStay this is yet to be pushed to the master branch. i have tried setattr() and instance as well. something just isn't clicking with me on this one.
models.py
class HostRegistration(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
address = models.CharField(max_length=200)
city = models.CharField(max_length=100)
state = models.CharField(max_length=30)
zipcode = models.CharField(max_length=15)
country = models.CharField(max_length=30)
# Override the __unicode__() method to return out something meaningful!
def __unicode__(self):
return self.user
forms.py
class HostForm(forms.ModelForm):
#no need for charfields here because we refrence the model with the fields
class Meta:
model = HostRegistration
fields = ['address', 'city', 'state', 'zipcode', 'country']
views.py - the problem starts here XD
# become a host
def host_register(request):
user = request.user
if user.is_authenticated:
if request.method == 'POST':
host_form = HostForm(data=request.POST)
if host_form.is_valid():
host_form.fields['user'].instance = user.id # this is where its failing.
host = host_form.save(commit=False)
print host
host.save()
return HttpResponseRedirect('/edit_userpage/')
else:
print host_form.errors
else:
return HttpResponseRedirect('/')
guide_form = HostForm()
context = {'guide_form': guide_form}
return render(request, 'users/host.html', context)
please let me know how to access the model object 'user' in my views and save the currently logged in user as a reference to it with the modelform. it would be great help.
i found the answer.
i changed my model.py to
class HostRegistration(models.Model):
# user is the changed variable
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
address = models.CharField(max_length=200)
city = models.CharField(max_length=100)
state = models.CharField(max_length=30)
zipcode = models.CharField(max_length=15)
country = models.CharField(max_length=30)
# Override the __unicode__() method to return out something meaningful!
def __unicode__(self):
return self.user
and i updated my views.py to:
def host_register(request):
user = request.user
if user.is_authenticated:
if request.method == 'POST':
host_form = HostForm(data=request.POST)
if host_form.is_valid():
instance = host_form.save(commit=False) # this is the trick.
instance.user = request.user # and this to get the currently logged in user
instance.save() # to commit the new info
return HttpResponseRedirect('/edit_userpage/')
else:
print host_form.errors
else:
return HttpResponseRedirect('/')
guide_form = HostForm()
context = {'guide_form': guide_form}
return render(request, 'users/host.html', context)
Does it work if you do host_form.cleaned_data.get("user") instead of host_form.fields['user'].instance?

django view Session ID -missing for user not logged in

On my current project I want the user to be able to fill in forms without having to sign up first (to make them more likely to use the service).
On the below view I'm trying to either save the registered user with the form data, or if the user isn't registered save the Session ID as a temporary user id.
However when I try to use the session ID it returns none. I'm not sure why the data is missing? (Session have the default django setup in apps and middleware as per the docs). Note when a user is logged in it seem to have a user id but not when no user is logged in.
View:
class ServiceTypeView(CreateView):
form_class = ServiceTypeForm
template_name = "standard_form.html"
success_url = '/'
def form_valid(self, form):
if self.request.user.is_authenticated():
form.instance.user = self.request.user
else:
form.instance.temp_user = self.request.session.session_key
super().form_valid(form)
online_account = form.cleaned_data['online_account']
if online_account:
return redirect('../online')
else:
return redirect('../address')
Model:
class EUser(models.Model):
supplier1 = models.OneToOneField(SupplierAccount)
supplier2 = models.OneToOneField(SupplierAccount)
supplier3 = models.OneToOneField(SupplierAccount)
online_account = models.BooleanField()
address = models.OneToOneField(Address, null=True)
temp_user = models.CharField(max_length=255, null=True)
user = models.OneToOneField(settings.AUTH_USER_MODEL, null=True, default=None)
class SupplierAccount(models.Model):
supplier = models.ForeignKey(Supplier)
username = models.CharField(max_length=255)
password = models.CharField(max_length=255)
Form:
class ServiceTypeForm(forms.ModelForm):
# BOOL_CHOICES = ((False, 'No'), (True, 'Yes'))
# online_account = forms.BooleanField(widget=forms.RadioSelect(choices=BOOL_CHOICES))
def __init__(self, *args, **kwargs):
super(ServiceTypeForm, self).__init__(*args, **kwargs)
self.fields['service_type'].initial = 'D'
class Meta:
model = EUser
fields = ('service_type', 'online_account')
The session key will exist if there is data set in the session dictionary already. Logged in users have a session key because Django stores authentication related data in the session by default, so a key will always be assigned because of that.
You can ensure that a key always exists by tossing some data into the session storage before trying to get the key.

Categories

Resources