After changing the field used as the primary key in a model, I now receive an error when trying to create an object from a form.
I have deleted the sqlite database file, everything in the migrations directory, and performed makemigrations and migrate. I do not believe the problem is with the database, rather something in the code no longer functions the same now that I am not using a custom primary key.
As someone new to Django, I suspect I am missing something fundamental but cannot quite identify what that is.
views.py
#login_required
def job_create(request):
client = request.POST.get('client')
form = JobForm(request.POST or None)
form.fields['client'].initial = Client.objects.get(client_name=client)
if request.method == "POST":
if form.is_valid():
form.save()
return JsonResponse({"Success": True})`
models.py
class Client(models.Model):
client_name = models.CharField(max_length=255, unique=True)
def __str__(self):
return self.client_name
class Job(models.Model):
client = models.ForeignKey(Client, on_delete=models.CASCADE)
job_number = models.CharField(validators=[RegexValidator(regex='^\d{4}$', message='Invalid job number', code='invalid')], max_length=4, unique=True)
job_description = models.CharField(max_length=30)
forms.py
class JobForm(forms.ModelForm):
class Meta:
model = Job
fields = ('client', 'job_number', 'job_description',)`
The above code fails to create and save the object into the database. Below is my attempt to recreate this using the Django shell:
>>> from myproject.models import Client, Job
>>> from myproject.forms import JobForm
>>> client = Client.objects.get(client_name='John')
>>> jobform = JobForm({'client': client, 'job_description':'This is a job description', 'job_number':'4321'})
>>> jobform.errors
{'client': ['Select a valid choice. That choice is not one of the available choices.']}
Database Columns
sqlite> PRAGMA table_info(myproject_job);
0|id|integer|1||1
1|job_number|varchar(4)|1||0
2|job_description|varchar(30)|1||0
3|client_id|integer|0||0
Solution for now
#login_required
def job_create(request):
if request.method == "POST":
client = Client.objects.get(client_name=request.POST.get("client"))
request.POST = request.POST.copy()
request.POST["client"] = client.id
form = JobForm(request.POST)
if form.is_valid():
form.save()
return JsonResponse({"success": "true"})
Related
I am trying to get user/customer info and copy it automatically to new model via custom form once requested but keep getting error. General overview as below.
(error : ValueError at /create_request/
Cannot assign "<SimpleLazyObject: <User: kambing>>": "OrderRequest.user" must be a "Customer" instance.)
model relationship between customer model and request model
my views.py as below:
from django.shortcuts import render, redirect
from .models import *
from accounts.models import *
from .forms import CreateRequestForm
from django.contrib.auth.decorators import login_required
from accounts.models import *
#login_required(login_url='login')
def createRequest(request):
form = CreateRequestForm()
if request.method =='POST':
form = CreateRequestForm(request.POST)
if form.is_valid():
order = form.save(commit=False)
order.user = request.user
order.save()
return redirect('/')
context = {'form':form}
return render(request, 'commerce_autoparts/request_form.html', context)
here my model.py(autopart_ecommerce): Same as shown in attached image
from django.db import models
from accounts.models import Customer
class Logistic(models.Model):
logistic_method = models.CharField(max_length=30, null=True,
blank=True)
def __str__(self):
return self.logistic_method
class OrderRequest(models.Model):
user = models.ForeignKey(Customer, on_delete=models.CASCADE,
null=True, blank=True)
ref_code = models.CharField(max_length=15, null=True, blank=True)
link = models.URLField(null=True, blank=True)
image = models.ImageField(upload_to='media/images',null=True,
blank=True)
price = models.FloatField(null=True)
draft = models.BooleanField(default=True)
logistic_method = models.ForeignKey(Logistic,
on_delete=models.CASCADE, null=True, blank=True)
note = models.TextField(max_length=100)
date_order = models.DateTimeField(auto_now=True)
I tried many solutions from stackoverflow but could not get it right. I am sorry if this not challenging question but I am stuck with this issue for some weeks. Some sort of guidance or reference available would be helpful.
I hope shared info is enough for your references.
Thank you
The problem is that it is a different model, so it doesn't like that. What you can do is copy the field data of the user to the customer, so you actually create new customer.
example:
# instead of this
order.user = request.user
order.save()
# you could do
new_user = Customer()
new_user.name = request.user.name
new_user.email = request.user.email
new_user.last_name = request.user.last_name
# then pass the user to the order
order.user = new_user
order.save()
I somehow manage to solve the issue. It was simply i don't refer the user to customer model. By adding the customer model (instance.user = request.user.customer), it automatically save the customer id once submitted. it may not be the perfect solution, but works for now..
#login_required(login_url='login')
def createRequest(request):
form = CreateRequestForm(request.POST)
if request.method =='POST':
form = CreateRequestForm(request.POST, request.FILES)
if form.is_valid():
instance = form.save(commit=False)
instance.user = request.user.customer
instance.ref_code = get_ref_code()
instance.save()
return redirect('/')
I am writing an application which stores "Jobs". They are defined as having a ForeignKey linked to a "User". I don't understand how to pass the ForeignKey into the model when creating it. My Model for Job worked fine without a ForeignKey, but now that I am trying to add users to the system I can't get the form to validate.
models.py:
from django.db import models
from django import forms
from django.contrib.auth.models import User
class Job(models.Model):
user = models.ForeignKey(User)
name = models.CharField(max_length=50, blank=True)
pub_date = models.DateTimeField('date published', auto_now_add=True)
orig_image = models.ImageField('uploaded image', upload_to='origImageDB/', blank=True)
clean_image = models.ImageField('clean image', upload_to='cleanImageDB/', blank=True)
fullsize_image = models.ImageField('fullsize image', upload_to='fullsizeImageDB/')
fullsize_clean_image = models.ImageField('fullsize clean image', upload_to='fullsizeCleanImageDB/')
regions = models.TextField(blank=True)
orig_regions = models.TextField(blank=True)
class JobForm(forms.ModelForm):
class Meta:
model = Job
In views.py I was creating the objects as follows:
if request.method == 'POST':
form = JobForm(request.POST, request.FILES)
if form.is_valid():
#Do something here
I understand that this passes the form data and the uploaded files to the form. However, I don't understand how to pass in a User to be set as the ForeignKey.
Thanks in advance to anyone who can help.
A typical pattern in Django is:
exclude the user field from the model form
save the form with commit=False
set job.user
save to database
In your case:
class JobForm(forms.ModelForm):
class Meta:
model = Job
exclude = ('user',)
if request.method == 'POST':
form = JobForm(request.POST, request.FILES)
job = form.save(commit=False)
job.user = request.user
job.save()
# the next line isn't necessary here, because we don't have any m2m fields
form.save_m2m()
See the Django docs on the model form save() method for more information.
Try:
if request.method == 'POST':
data = request.POST
data['user'] = request.user
form = JobForm(data, request.FILES)
if form.is_valid():
#Do something here
I've created a Django view that does 2 things:
Create a new account
Modify a account
Works:
Creating new account and submitting the HTML form data to the database. Also works: showing a prefilled HTML form if user wants to modify an account with the account data that is known in the database.
Doesnt work:
When the user submits his/her form to update an account (user modified the info in the form), nothing is updated in the database.
I know how to update one single static value in the database like so:
a = accounts.objects.filter(pk=account_id).update(name='static value here')
but I don't know how to update the database with all the form data that the user submits when using Django Modelforms. Does anyone knows how to update the database with the submitted form data?
Code
#login_required(login_url='/dashboard/')
def dashboard_accounts_new_modify(request, account_id=None):
if request.method == 'POST':
# POST DETECTED
form = MyModelForm(request.POST, request.FILES)
if account_id:
# POST DETECTED
# ACCOUNT ID FOUND
# USER WANTS TO MODIFY A ACCOUNT
# WITH THIS QUERY I CAN UPDATE 1 STATIC VALUE IN THE DATABASE
# HOW DO I UPDATE THE VALUES FROM THE FORM IN THE DATABASE?? :(
a = accounts.objects.filter(pk=account_id).update(name='static value here')
return HttpResponseRedirect('/dashboard/accounts/')
else:
# POST DETECTED
# ACCOUNT ID NOT FOUND
# USER WANTS TO CREATE A NEW ACCOUNT
if form.is_valid():
if request.POST.get("name").lower() == 'new':
raise Http404("New account name may not be named NEW.")
# DATAHASE QUERY: ADD NEW ACCOUNT TO DATABASE
form.save()
# REDIRECT
return HttpResponseRedirect('/dashboard/accounts/')
elif account_id:
# NO POST DETECTED
# ACCOUNT ID FOUND
# PREFILL FORM WITH DATA
try:
from django.forms.models import model_to_dict
a = accounts.objects.get(pk=account_id)
form = MyModelForm(initial=model_to_dict(a))
except:
raise Http404("Account not found.")
else:
# NO POST DETECTED
# MODIFICATION IS NOT DETECTED
# LOAD EMPTY FORM
form = MyModelForm()
return render(request, 'backend/base_accounts_new.html', {'Title': 'Accounts', 'form' : form})
Model
# Clientdatabase
class accounts(models.Model):
name = models.CharField(max_length=200)
url = models.CharField(max_length=200)
website_title = models.CharField(max_length=200)
website_h1_text = models.CharField(max_length=200)
website_h2_text = models.CharField(max_length=200)
website_search_text = models.CharField(max_length=200)
website_font = models.CharField(max_length=200)
website_footer_left = models.CharField(max_length=600)
website_footer_right = models.CharField(max_length=600)
website_color_code_search_button = models.CharField(max_length=200)
website_color_code_banner = models.CharField(max_length=200)
website_logo_height_pixels = models.PositiveIntegerField()
website_logo_width_pixels = models.PositiveIntegerField()
filepath_favicon = models.FileField()
filepath_logo_vector = models.FileField()
filepath_logo_normal = models.FileField()
filepath_background_1 = models.FileField()
filepath_background_2 = models.FileField(blank=True, null=True)
filepath_background_3 = models.FileField(blank=True, null=True)
filepath_background_4 = models.FileField(blank=True, null=True)
setting_background_1_active = models.BooleanField()
setting_background_2_active = models.BooleanField()
setting_background_3_active = models.BooleanField()
setting_background_4_active = models.BooleanField()
def __str__(self):
return self.name
class AccountsForm(ModelForm):
class Meta:
model = accounts
fields = '__all__'
You can do like:
from django.shortcuts import get_object_or_404
if request.method == 'POST':
if account_id::
account = get_object_or_404(accounts, pk=account_id)
form = MyModelForm(request.POST,request.FILES, instance=account)
if form.is_valid():
...
form.save()
return HttpResponseRedirect('/dashboard/accounts/')
else:
form = MyModelForm(request.POST, request.FILES)
if form.is_valid():
if request.POST.get("name").lower() == 'new':
raise Http404("New account name may not be named NEW.")
form.save()
Learn more about forms here
I created an app to audit operations on objects. When an object is created, updated or deleted, a new record is created in my audit table (Auditor model).
The Audit app is working now, but to use it on my models requires some work and a lot of code that I believe can be avoided with a most optimized approach.
Which django resources or approach can I use to integrate the Audit app on my models, without write so much code? I need a simplified way to archieve this integration of the Auditor app with all my models and other projects.
I'll give an example how I using the Auditor model with a model (Car) that I want to audit.
Thank you.
Car model
class Car(models.Model):
name = models.CharField(max_length=50)
brand = models.CharField(max_length=50)
color = models.CharField(max_length=50)
is_available = models.BooleanField(default=True)
Auditor model
class Auditor(models.Model):
field = models.CharField(max_length=255, blank=True)
action = models.CharField(max_length=6)
old_value = models.TextField(blank=True)
new_value = models.TextField(blank=True)
stamp = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL)
content_type = models.ForeignKey(ContentType, blank=True)
object_id = models.PositiveIntegerField(blank=True)
content_object = GenericForeignKey('content_type', 'object_id')
deleted_object = models.CharField(max_length=255, blank=True)
Car view
from audittest.apps.auditor.models import Auditor
from django.contrib.contenttypes.models import ContentType
#Function for audit creation. I know that the view is not the right place to put this function, but I put this here for test.
def create_audit(obj, request, action, obj_id=False):
if action == 'CREATE':
audit = Auditor(content_type = ContentType.objects.get_for_model(obj), object_id = obj.id, user = request.user, action = action)
elif action == 'DELETE':
audit = Auditor(content_type = ContentType.objects.get_for_model(obj), object_id = obj_id, user = request.user, action = action, deleted_object = obj)
audit.save()
def new(request, template_name='cars/form.html'):
form = CarForm(request.POST or None)
if form.is_valid():
obj = form.save()
create_audit(obj, request, 'CREATE')
return redirect('car:admin')
return render(request, template_name, {'form':form, 'title':u'Novo Car'})
def edit(request, pk, template_name='cars/form.html'):
car = get_object_or_404(Car, pk=pk)
form = CarForm(request.POST or None, instance=car, request=request)
if form.is_valid():
form.save()
return redirect('car:admin')
return render(request, template_name, {'form':form,'title':u'Editar Car'})
def delete(request, pk, template_name='cars/confirm_delete.html'):
car = get_object_or_404(Car, pk=pk)
obj_id = car.id
if request.method=='POST':
car.delete()
create_audit(car, request, 'DELETE', obj_id=obj_id)
messages.success(request, u'Car excluĂdo com sucesso.')
return redirect('car:admin')
return render(request, template_name, {'object':car})
Car form
class CarForm(ModelForm):
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
super(CarForm, self).__init__(*args, **kwargs)
def clean(self):
cleaned_data = super(CarForm, self).clean()
# Audit updated fields
if self.instance.pk is not None:
fields = []
for field in self.instance._meta.get_all_field_names():
if field != 'id' and getattr(self.instance, field) != cleaned_data[field]:
#fields.append((field, getattr(self.instance, field), cleaned_data[field]))
audit = Auditor(content_type = ContentType.objects.get_for_model(self.instance), object_id = self.instance.pk, user = self.request.user, action = 'UPDATE', field = self.instance._meta.get_field(field).verbose_name, old_value = getattr(self.instance, field), new_value = cleaned_data[field])
audit.save()
return cleaned_data
Use class based views in your applications so you can use the full advantages of the cool Mixins,
I usually create a Mixin that can be added to any CreateView or UpdateView.
class withAudit(object):
"""
A mixin that will create an audit record wither the action is
Create or Update
"""
def get_success_url(self):
"""
This will be called when the form is valid and saved.
"""
# create the record
audit = Auditor(content_type= ContentType.objects.get_for_model(self.model))
audit.object_id = self.object.pk
audit.user = request.user
# You will need a way to capture this action.
# there are many ways to do it.
audit.action = "Create"
audit.save()
return super(withAudit, self).get_success_url()
In your views you have to use it this way
class CarCreate(withAudit, CreateView):
model = Car
For update
class CarUpdate(withAudit, UpdateView):
model = Car
You can do the following to any UpdateView or CreateView in your application. However, For Deleting the object, I think you will need another mixin which will capture the data before performing the action. You need to see the class based views docs in order to customise these as you want.
The same idea can be done with decorators if you really want keep using method based views.
If you have a big application with high traffic, this process should be done in the background where you define a stack or queue an you keep passing these information to it, which will provide a better performance indeed. Some big applications using another database for logs and audit.
I have a ModelChoiceField called outage_name. I also have a simple form that allows you to select the item from the list. The ModelChoiceField is pulled from a MySQL DB. This queryset is located in forms.py
outage_name = forms.ModelChoiceField(queryset = Outage.objects.filter(published = True)
The models.py is listed below.
from django.db import models
from django.contrib.auth.models import User
class Outage(models.Model):
outage_name = models.CharField(max_length=60, unique=True)
published = models.BooleanField()
def __unicode__(self):
return self.outage_name
class Detail(models.Model):
detail = models.CharField(max_length=60, unique=True)
user = models.ForeignKey(User)
outage = models.ForeignKey(Outage)
def __unicode__(self):
return self.outage
When I select from the list and submit the form I can't seem to figure out how to match outage = models.ForeignKey(Outage) that was selected on the list. To the correct outage_name. In my views.py I can hard code the id and it submits to the database and everything works fine.
def turnover_form(request):
if request.user.is_authenticated():
if request.method == 'POST':
form = TurnoverForm(request.POST)
if form.is_valid():
details = Detail.objects.get_or_create(
detail = form.cleaned_data['detail'],
user = request.user,
outage = Outage.objects.get(pk=1))
return HttpResponseRedirect('/turnover/user/')
else:
form = TurnoverForm()
variables = RequestContext(request, {'form': form})
return render_to_response('turnover_form.html', variables)
else:
return HttpResponseRedirect('/authorization/')
Any advice on how to match the id with the selected item would be appreciated. I'm sure my code is not very pythonic as I'm still learning.
outage = form.cleaned_data['outage'] # cleaned_data['outage'] is a model instance