I created a system with Django. In this system, user uploads an excel table and I creating a new customer from that excel. But in this excel I have 2 important columns. They are entity and parent. I want to when a user uploads this excel table but If there is an entity or parent that is not registered in my database, I want to create it and then save it. I user get_or_createe for that but I am getting an error:
ValueError at /customers/upload Cannot assign "(<ParentCompany:
TESTP>, False)": "Customer.parent" must be a "ParentCompany" instance.
How can I solve it?
views.py
def customer_excel_upload(request):
current_user = request.user
userP = UserProfile.objects.get_or_create(username=current_user)
company = userP[0].company
if request.method == 'POST':
form = CustomerExcelForm(request.POST, request.FILES)
if form.is_valid():
new_excel = form.save()
new_excel = new_excel.excel
df = pd.read_excel('C:/fray/otc/'
+
new_excel.name,
index_col=0,
engine='openpyxl')
for index, row in df.iterrows():
if row is not None:
new_customer = Customer()
new_customer.customer_name = index
country = Country.objects.get(country_name=row['Country'])
new_customer.address = row['Address']
new_customer.customer_number = row['Customer Number']
new_customer.phone_number = row['Phone Number']
new_customer.email_address = row['Email Adress']
new_customer.credit_limit = row['Credit Limit']
new_customer.currency_choice = row['Currency choice']
new_customer.risk_rating = row['Risk rating']
parent = ParentCompany.objects.get_or_create(parent=row['Parent Company'],
company=request.user.company)
entity = Entities.objects.get_or_create(entities=row['Entity'], company=request.user.company)
new_customer.parent = parent
new_customer.entity = entity
new_customer.country = country
new_customer.company = company
new_customer.save()
return redirect('home')
else:
form = CustomerExcelForm()
context = {
'form': form
}
return render(request, 'customer_excel_upload.html', context)
models.py
class Customer(models.Model):
customer_name = models.CharField(max_length=100)
country = models.ForeignKey(Country, on_delete=models.CASCADE, null=True, unique=False)
address = models.CharField(max_length=250),
...
parent = models.ForeignKey(ParentCompany, on_delete=models.CASCADE, blank=True, null=True)
entity = models.ForeignKey(Entities, on_delete=models.CASCADE, blank=True, null=True)
class ParentCompany(models.Model):
parent = models.CharField(max_length=50, null=True)
company = models.ForeignKey(CompanyProfile, on_delete=models.CASCADE, null=True, unique=False)
class Entities(models.Model):
entities = models.CharField(max_length=250)
company = models.ForeignKey(CompanyProfile, on_delete=models.CASCADE, null=True, unique=False)
get_or_create doesn't return an object directly, as the error message says. You can check the docs. It returns a tuple of (object, created).
Yours is returning correctly
(<ParentCompany: TESTP>, False)
You just need to get the first part.
you can change your get_or_create statements to get the first like this:
ParentCompany.objects.get_or_create(parent=row['Parent Company'], company=request.user.company)[0]
note the [0] on the end.
Related
I am trying to write a row to database, with data gathered in a form. I need to work with two foreign keys and one of them is causing the creating to fail, although I am unable to figure out why:
here is my model:
def upload_path(instance,file):
file_dir = Path(file).stem
print('usr',instance.user.id)
path = '{}/{}/{}/{}'.format(instance.user.id,"projects",file_dir,file)
return path
class BuildingFilesVersions(models.Model):
version_id = models.AutoField(primary_key=True)
building_id = models.ForeignKey(Building, on_delete=models.CASCADE,related_name='building_id_file')
user = models.ForeignKey(Building, on_delete=models.CASCADE,related_name="user_file")
created_at = models.DateTimeField(auto_now_add=True, blank=True)
description = models.TextField(max_length=200, blank=True, null=True)
modification_type = models.CharField(choices=WORK_TYPE_CHOICES, max_length=200, blank=True, null=True)
filename = models.CharField(max_length=200, blank=True, null=True)
file = models.FileField(upload_to=upload_path, null=True, blank=True)
and here is my view:
#login_required
#owner_required
def RegisterFileView(request,pk):
form = AddBuildingFileForm()
if request.method == 'POST':
form = AddBuildingFileForm(request.POST,request.FILES)
if form.is_valid():
description = form.cleaned_data["description"]
modification_type = form.cleaned_data["modification_type"]
filename = form.cleaned_data["modification_type"]
file = request.FILES['file'].name
BuildingFilesVersions.objects.create(building_id_id=pk,
user_id=request.user,
description=description,
modification_type=modification_type,
filename=filename,
file=file)
return redirect('home')
else:
form = AddBuildingFileForm()
context = {'form':form}
return render(request, 'building_registration/register_file.html', context)
what gets me confused is that the error is Field 'building_id' expected a number but got <SimpleLazyObject: <User: Vladimir>> even though pk return the proper building_id
Can anyone see where I messed up?
to access the id of the foreign key add a double underscore
BuildingFilesVersions.objects.create(building_id=Building.objects.get(pk=pk),
user=request.user,
description=description,
modification_type=modification_type,
filename=filename,
file=file)
Your user must be logged in to assign him in the Model
Answer for Similar Question
See the Docs
This is my models.py file
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class Book(models.Model):
category_choices =(
#("Undefined","Undefined"),
("Action", "Action"),
("Romance", "Romance"),
("Horror", "Horror"),
("Comedy", "Comedy"),
("Adventure", "Adventure"),
("Dramatic", "Dramatic"),
("Crime","Crime"),
("Fantasy","Fantasy"),
)
name = models.CharField(max_length=100)
author = models.CharField(max_length=100, null=True)
content = models.TextField()
price = models.DecimalField(max_digits=5, decimal_places=2)
image = models.ImageField(upload_to= 'photos/%y/%m/%d', blank = True)
category = models.CharField(
max_length = 20,
choices = category_choices,
#default = 'Undefined'
)
publication_year = models.CharField(max_length=4, null=True)
ISBN = models.CharField(max_length=13, null=True, unique=True)
active = models.BooleanField(default= True)
def __str__(self):
return self.name
class Borrow(models.Model):
name = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
book = models.OneToOneField(Book, null=True, on_delete= models.SET_NULL)
period = models.PositiveIntegerField(default=0)
id = models.IntegerField(primary_key=True)
def __str__(self):
return str(self.book)
and this is my forms.py file
from django import forms
from .models import Borrow
class BorrowForm(forms.ModelForm):
class Meta:
model = Borrow
fields = ('name', 'book', 'period')
and this is the function in my views.py file that renders the form
#login_required
def borrowing(request):
momo = BorrowForm()
if request.method == 'POST':
momo = BorrowForm(request.POST)
if momo.is_valid():
instacne = momo.save(commit=False)
instacne.user = request.user.username
instacne.save()
return redirect('profile')
return render(request, 'books/book.html', {'momo': momo})
The role of this function is to render that form and to save the data that user will enter and automatically assign the username of the current user to the field 'name' in form.
I tried alot of things to get the username of the current user and assign it to the field 'name' but nothing works and that field stays blank.
You're using a models.ForeignKey(User) so that table will store a user id, not a username. I'd call this field user and not name, personally.
Therefore you need to provide a user instance to it like this;
#login_required
def borrowing(request):
initial = {}
if request.user.is_authenticated:
initial.update({'name': request.user})
momo = BorrowForm(initial=initial)
if request.method == 'POST':
momo = BorrowForm(request.POST)
if momo.is_valid():
instance = momo.save(commit=False)
instance.user = request.user
instance.save()
If you wanted to easily get the username for a Borrow instance you could do this;
class Borrow(models.Model):
name = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
book = models.OneToOneField(Book, null=True, on_delete= models.SET_NULL)
period = models.PositiveIntegerField(default=0)
id = models.IntegerField(primary_key=True)
def __str__(self):
return str(self.book)
#property
def username(self):
return self.name.username
If you want the form to offer users by username, you can either have the str method of your user model return username, or create custom choices as a tuple of user ID & username in the form __init__
here is my model.py
class Product(models.Model):
user = models.ForeignKey(User, null=True, blank=True,on_delete=models.CASCADE)
coinid = models.CharField(max_length=255, unique = True, null=True)
digit = models.CharField(max_length=18, unique = True, null=True)
ctp = models.FloatField(max_length=100, null=True)
transection_id = models.IntegerField(null=True, default=0)
slug = models.SlugField(max_length=250, null=True, blank=True)
date_created = models.DateField(auto_now_add=True, null=True)
when the user fills the form which includes = coinid and ctp.
I want Django to automatically fill Digit(unique) and user(logged in) fields for me when updated to the products table.
here is my form.py
class CreateProduct(ModelForm):
class Meta:
model = Product
fields = ['user','coinid', 'ctp']
exclude = ['user']
views.py
def createProduct(request):
user_id = request.user.customer
form = CreateProduct(instance=user_id)
if request.method == 'POST':
form = CreateProduct(request.POST)
if form.is_valid():
form.save()
return redirect('/products/')
context = {'form': form}
return render(request, 'accounts/newcoin.html', context)
Also, I want to validate the product's ctp value whenever a user types in the unique digit to another form.
you can override the save method on the Product model to achieve what you want
example: models.py
import uuid
class Product(models.Model):
user = models.ForeignKey(User, null=True,
blank=True,on_delete=models.CASCADE)
coinid = models.CharField(max_length=255, unique = True, null=True)
digit = models.CharField(max_length=18, unique = True, null=True)
ctp = models.FloatField(max_length=100, null=True)
transection_id = models.IntegerField(null=True, default=0)
slug = models.SlugField(max_length=250, null=True, blank=True)
date_created = models.DateField(auto_now_add=True, null=True)
def save(self, *awags, **kwargs):
# if coinid and ctp is not none
# save a uuid in the digit field
if self.coinid and self.ctp:
self.digit = str(uuid.uuid4())
# Your code to change the user model here
# exmaple to change user name
user = User.objects.get(id = self.user.id)
user.username = "Stackoverflow user"
user.save()
super().save(*awags, **kwargs)
You can check the Django docs for reference to override Save method
For creating unique IDs you can use the python uuid module
import uuid
unique_id = str(uuid.uuid4())
You can also use uuid.uuid1() however the docs recommends uuid4 for a random unique string
You can check the docs for uuid here
I have a situation again, when I do a form.save(), my form saves only the parent table, it does not save the intermediary table which is required for Many-To-Many relationships.
My models.py look like this
class Platform(models.Model):
id = models.AutoField(primary_key=True)
description = models.TextField(blank=True)
annotation_file_archived_location = models.FileField(upload_to='msrb/platform')
anntation_file_hashsum = models.TextField()
annotation = models.TextField(unique=True)
def __unicode__(self):
return self.annotation
class Meta:
managed = True
db_table = 'platform'
class Dataset(models.Model):
dataset_id = models.TextField(primary_key=True)
title = models.TextField(blank=True, null=True)
taxonomy = models.ForeignKey('Organism', blank=True, null=True)
citation = models.TextField(blank=True, null=True)
summary = models.TextField(blank=True, null=True)
contributor = models.TextField(blank=True, null=True) # This field type is a guess.
submitted = models.DateField(blank=True, null=True)
last_updated = models.DateField(blank=True, null=True)
author = models.ForeignKey('Users', db_column='author', blank=True, null=True)
platforms = models.ManyToManyField(Platform,through='DatasetPlatform')#,through_fields=('Platform:platform','dataset'))
class Meta:
managed = True
db_table = 'dataset'
class DatasetPlatform(models.Model):
id = models.IntegerField(primary_key=True)
platform = models.ForeignKey(Platform, null=False)
dataset = models.ForeignKey(Dataset,null=False)
class Meta:
managed = False
db_table = 'dataset_platform'
Forms.py
class DatasetForm(forms.ModelForm):
dataset_id = forms.CharField(required=True,help_text="dataset_id")
title = forms.CharField(required=True,help_text="title")
taxonomy = forms.ModelChoiceField(queryset=Organism.objects.all(),empty_label=None,help_text='Taxonomy')
citation = forms.CharField(required=True,help_text="citation")
summary = forms.CharField(required=True,help_text="summary")
contributor = forms.CharField(help_text="contributor (separated by comma)")
submitted = forms.DateField(initial = datetime.now,required=True,help_text="Submitted date")
last_updated = forms.DateField(initial = datetime.now,required=True,help_text="Last Updated date")
platform = forms.ModelMultipleChoiceField(queryset=Platform.objects.all(),help_text="Choose the platforms this dataset belongs to")
class Meta:
model = Dataset
fields = ('dataset_id','title','taxonomy','citation','summary','contributor','submitted','last_updated','platform')# Add author later ,'author')
views.py
def add_dataset(request):
context_dict = {}
if request.method == 'POST':
form = DatasetForm(request.POST)
if form.is_valid():
print "------------------------------------------------------------------------------"
print form.cleaned_data['platform']
form.save()
print "------------------------------------------------------------------------------"
return HttpResponseRedirect('/msrb/')
else:
print form
print form.errors
else:
form = DatasetForm()
context_dict['form'] = form
template = get_template('msrb/add_dataset.html')
context = RequestContext(request,context_dict)
return HttpResponse(template.render(context))
I have tried saving the data using
form.save(commit=True)
form.save_m2m()
form.cleaned_data gives the proper output.
I am not sure what am I missing here as I dont get an error message from django too.
EDIT
I have a workaround for the problem, but I am not sure if this is the best solution. If I can get a better solution, I will be greatful.
def add_dataset(request):
context_dict = {}
if request.method == 'POST':
form = DatasetForm(request.POST)
if form.is_valid():
print form.cleaned_data['platform']
f = form.save()
for p in form.cleaned_data['platform']: <--- Added
d = DatasetPlatform(dataset = f,platform = p) <--- Added
d.save() <--- Added
return HttpResponseRedirect('/msrb/')
else:
print form
print form.errors
else:
form = DatasetForm()
context_dict['form'] = form
template = get_template('msrb/add_dataset.html')
context = RequestContext(request,context_dict)
return HttpResponse(template.render(context))
Django is not able (well, refuses) to automatically save m2m relations with a custom through model. Saving the form data uses direct assignment to the ManyToManyField, which will not work as explained here.
If removing the custom through model is an option, I'd do that. Granted, it will have to be managed = True, but it greatly simplifies use of the field. You're not saving any extra data in the relationship, so it might be an option.
Otherwise, you have already found the only workaround. Each time you want to manipulate the m2m relationship, you'll have to manually create, alter and delete the DatasetPlatform instances. Again, this is explained in further detail in the relevant documentation.
I have a form that takes information about an item and saves it into the database. Im trying to allow users to edit that form with new/different information and save it again. Im having some difficulty trying to get this to work. I think the problem is when Django validates the data it sees that the slug and unique id of the item already exist and doesnt allow it to validate the data but im not completely sure about this as well. Would really appreciate the help. Thanks.
#view
def edit_item(request, item_id):
if request.method == 'POST':
item = Item.objects.get(id=item_id)
form = AddItem(request.POST,instance=item)
if form.is_valid():
item = form.save(commit=False)
item.user = request.user
item.is_active = True
item.slug = slugify(item.name)
item.save()
return HttpResponseRedirect('thanks.html')
else:
form = AddItem(instance=item )
return render_to_response('forsale.html', locals(), context_instance=RequestContext(request))
#form
class AddItem(forms.ModelForm):
name = forms.CharField(label="Title")
class Meta:
model = Item
exclude = ('user','slug','is_active',)
#model
class Item(models.Model):
user = models.ForeignKey(User)
name = models.CharField(max_length=30)
slug = models.SlugField(max_length=50, unique=True)
is_active = models.BooleanField(default=True, blank=True)
image1 = models.ImageField(upload_to='img')
image2 = models.ImageField(upload_to='img', blank=True)
image3 = models.ImageField(upload_to='img', blank=True)
image_caption1 = models.CharField(max_length=200, blank=True)
image_caption2 = models.CharField(max_length=200, blank=True)
image_caption3 = models.CharField(max_length=200, blank=True)
price = models.DecimalField(max_digits=8, decimal_places=2)
quantity = models.IntegerField(default=1)
description = models.TextField()
created = models.DateTimeField(auto_now_add=True)
shipping_price = models.DecimalField(decimal_places=2, max_digits=6)
categories = models.ManyToManyField(Category)
def save(self, *args, **kwargs):
super(Item, self).save(*args, **kwargs)
if not self.slug:
self.slug = slugify(self.product.title) + "-" + str(self.id)
self.save()
Update your view function like this to return form for get request as well:
def edit_item(request, item_id):
if request.method == 'POST':
item = Item.objects.get(id=item_id)
....
#your existing code
else: #if its GET request
item = Item.objects.get(id=item_id)
form = AddItem(instance=item )
return render_to_response('forsale.html', locals(),
context_instance=RequestContext(request))
Note: you need to handle case when item with item_id does not exists in the DB. In that case do not use instance parameter to instantiate the form.