Django add model instance data to widget in forms.py - python

forms.py
from django.forms import *
from .models import client, notes, contractor, cNotes, personnel, pNotes, estimate, job
class clientForm(self, ModelForm):
class Meta():
model = client
fields = ("contractor", "firstName", "lastName", "phoneNumber", "email", "gateCode")
#inst = client.objects.get(pk=pk)
widgets = {
'firstName': TextInput(attrs={'value' : self.instance.firstName}), 'lastName': TextInput(attrs={'value' : self.instance.lastName})
}
self.instance.firstName Needs to fetch the current first name of that instance in the model so the value of the widget when the edit form view is open is the current value (Note: setting initial= doesn't work for what I want). I have tried many Solutions but I can't seem to get any of them to work.
Edit: I tried overriding init
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['firstName'].widget.attrs.update(
{'value': self.instance.firstName},
)
but when I do this it says self is not defined.
Edit: I also tried passing the instance data by getting it in the view and passing it to the form by overriding get_form_kwargs
models.py
class client(models.Model):
#model representing client
contractor = models.ForeignKey('contractor', on_delete=models.CASCADE, blank=True, null=True)
firstName = models.CharField(max_length=50, help_text='Enter first name of client', null=True, blank=True)
lastName = models.CharField(max_length=50, help_text='Enter last name of client', null=True, blank=True)
phoneNumber = models.CharField(max_length=16, help_text='Enter number for client', null=True, blank=True)
email = models.EmailField(max_length=50, help_text='Enter email for client', blank=True, null=True)
gateCode = models.CharField(max_length=50, help_text='Enter gate code for client', blank=True, null=True)
fullName = models.CharField(max_length=50, help_text='', blank=True, null=True)
client_id = models.SlugField(max_length=50, help_text='', primary_key=True)
initialContactDate = models.DateTimeField(auto_now_add=True, blank=True, null=True)
views.py
def eClientF(request, pk=None):
inst = client.objects.get(pk=pk)
if request.method == "POST":
client_form = clientForm(request.POST, instance=inst)
estimate_form = estimateForm(request.POST, instance=inst)
notes_form = notesForm(request.POST, instance=inst)
if(client_form.is_valid() and estimate_form.is_valid() and notes_form.is_valid()):
cli = client_form.save()
estimate2 = estimate_form.save(False)
notes2 = notes_form.save(False)
estimate2.client = cli
notes2.client = cli
estimate2.save()
notes2.save()
return redirect('/main/client')
else:
client_form = clientForm(instance=inst)
estimate_form = estimateForm(instance=inst)
notes_form = notesForm(instance=inst)
args = {}
args["client_form"] = client_form
args["estimate_form"] = estimate_form
args["notes_form"] = notes_form
return render(request, "main/eClformTemp.html", args)
models.py and views.py have been reduced to relevant code only
Edit: I have modified views.py as suggested, but when I try using this form it doesn't load the page and says self is not recognized referring to froms.py where it says TextInput(attrs={'value' : self.instance.firstName})
Edit:
It won't though, I know when you past instance when creating the form in views it should populate but it doesn't. I have referenced the docs and many examples to confirm I was doing it correctly and I was but it wasn't working. To work around it I ran the server and opened the form then I inspected element and copied the html and manually added value to the html and used what I copied as the template to ensure this workaround will work. However the template cannot be manually made because it wouldn't properly reflect changes in the database. I just need to know how I can pass the current model instance into the widget attrs for value. Ik I will have to edit init and update the widget that way but my implementation wouldn't work so I'm asking how to do it correctly.

Related

Integrity Error NOT NULL constraint failed even though I have set blank=True, null=True in my model

Im getting a NOT NULL constraint error in my code when trying to save my model form, even though the fields I have left empty are optional (have set blank=True, null=True) in models.py
Im very confused, what am I doing wrong?
The error is popping up when I leave the first optional field blank (description). Filling any of them manually before work.save() pushes the issue to the next field, and passes when all fields are filled.
EDIT: this also happens when trying to create a work instance from the admin dashboard.
models.py
class Work(models.Model):
## core fields
creator = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True,
default=None)
created = models.DateTimeField()
modified = models.DateTimeField()
work_slug = models.SlugField(max_length=50) # slug -> TBD: find way to assign default value to slug = archival number.
archive = models.ForeignKey(Archive, on_delete=models.CASCADE)
# superfolder -> replaces category, series etc with dynamic hierarchical database
folder = models.ForeignKey(Folder, on_delete=models.CASCADE)
# basic metadata fields
name = models.CharField(max_length=50)
year = models.CharField(max_length=50)
medium = models.CharField(max_length=50)
description = models.CharField(max_length=1200, blank=True, null=True)
# optional metadata
authors = models.CharField(max_length=50, blank=True, null=True)
classification = models.CharField(max_length=50, blank=True, null=True)
location = models.CharField(max_length=50, blank=True, null=True)
link = models.URLField(max_length=50, blank=True, null=True)
record_creator = models.CharField(max_length=50, blank=True, null=True) # revisit ->
# custom descriptors
cd1_name = models.CharField(max_length=50, blank=True, null=True)
cd1_value = models.CharField(max_length=50, blank=True, null=True)
cd2_name = models.CharField(max_length=50, blank=True, null=True)
cd2_value = models.CharField(max_length=50, blank=True, null=True)
cd3_name = models.CharField(max_length=50, blank=True, null=True)
cd3_value = models.CharField(max_length=50, blank=True, null=True)
cd4_name = models.CharField(max_length=50, blank=True, null=True)
cd4_value = models.CharField(max_length=50, blank=True, null=True)
cd5_name = models.CharField(max_length=50, blank=True, null=True)
cd5_value = models.CharField(max_length=50, blank=True, null=True)
cd6_name = models.CharField(max_length=50, blank=True, null=True)
cd6_value = models.CharField(max_length=50, blank=True, null=True)
cd7_name = models.CharField(max_length=50, blank=True, null=True)
cd7_value = models.CharField(max_length=50, blank=True, null=True)
# Standardized Metadata
# Methods
def __str__(self):
return 'Work: {}'.format(self.name)
def save(self, *args, **kwargs):
''' On save, update timestamps '''
user = get_current_user()
if not self.id: # if the model is being created for the first time:
self.creator = user # assign the currently logged in user as the creator
self.created = timezone.now() # set the 'created' field to the current date and time
# self.slug = **archival id of work (automatically determined)**
self.modified = timezone.now() # set the modified field to the current date and time. This is reassigned everytime the model is updated.
return super(Work, self).save(*args, **kwargs)
forms.py
class WorkForm(ModelForm):
class Meta:
model = Work
fields = ['name', 'year', 'medium', 'description', 'authors', 'classification', 'location', 'link', 'record_creator', 'cd1_name', 'cd1_value', 'cd2_name', 'cd2_value', 'cd3_name', 'cd3_value', 'cd4_name', 'cd4_value', 'cd5_name', 'cd5_value', 'cd6_name', 'cd6_value', 'cd7_name', 'cd7_value']
views.py
def add_work(request, folder_pk):
'''
Add a work to the filesystem.
folder_pk: the primary key of the parent folder
Checks if the user is logged in and if the user is the creator of the folder. If so, the user is allowed to add a work to the folder. Otherwise, the user is redirected to the login page.
'''
# add work to the database
parent = Folder.objects.get(pk=folder_pk)
mediaFormSet = modelformset_factory(MediaFile, fields=('name', 'alt_text', 'caption', 'media'), extra=1)
if request.method == "POST" and parent.archive.creator == get_current_user():
# if the form has been submitted
# Serve the form -> request.POST
form = WorkForm(request.POST)
# mediaFormSet = mediaFormSet(request.POST)
if form.is_valid(): # if the all the fields on the form pass validation
# Generate archival ID for work
# archival ID is random, unique 6 digit number that identifies the work
archival_id = get_archival_id()
# create a new work with the parameters retrieved from the form. currently logged in user is automatically linked
work = form.save(commit=False)
work.work_slug = archival_id
work.folder=parent
work.archive=parent.archive
work.save()
# Redirect to dashboard page
return redirect('add_media_to_work', work_pk=work.pk)
else:
# If the form is not submitted (page is loaded for example)
# -> Serve the empty form
form = WorkForm()
return render(request, "archival/add_edit_work.html", {"workForm": form})
i was facing the same problem as you, i made a sign up form, and it was giving me the same error because the .save() method was getting executed before i fill in the fields, there was no data to save, because of that: the fields type was None. so i just implemented an if else statement to make sure that the .save() method won't be executed if the type of the field isnNone,
here is a snippet:
if field == None:
pass
else:
form.save()

Django-filter how to show only some objects on dropdown?

My site simply works like this: every Manager can have some SubManagers, those SubManagers can have some Agents (so the Agents are indirectly related to the Manager, see models.py to understand better the relations between them). I want to show in the Manager's profile page (see views.py) all the MembershipCard created by his/her related Agents. I'm trying to implement a filter to search, for example, cards created by a specific Agent, i'm able to do this but i would like to show in the dropdown only the Agents related to the Manager, the dropdown list now shows all Agents in the database
models.py
class StandardProfile(models.Model):
name = models.CharField(max_length=200, null=True)
surname = models.CharField(max_length=200, null=True)
phone_number = models.CharField(max_length=200, null=True)
class Meta:
abstract = True
class Manager(StandardProfile):
user = models.OneToOneField(User, null=True, on_delete=models.CASCADE)
class SubManager(StandardProfile):
user = models.OneToOneField(User, null=True, on_delete=models.CASCADE)
manager = models.ForeignKey(Capo, null=True, on_delete = models.SET_NULL)
class Agent(StandardProfile):
user = models.OneToOneField(User, null=True, on_delete=models.CASCADE)
manager = models.ForeignKey(Manager, null=True, on_delete = models.SET_NULL)
subManager = models.ForeignKey(SubManager, null=True, blank=True, on_delete = models.SET_NULL)
class MembershipCard(models.Model):
agent = models.ForeignKey(Agent, null=True,blank=True, on_delete = models.SET_NULL)
client = models.ForeignKey(Client, null=True,blank=True, on_delete = models.SET_NULL)
creation_date = models.DateTimeField(auto_now_add=True, null=True)
activation_date = models.DateTimeField(null=True,blank=True)
expiration_date = models.DateTimeField(null=True,blank=True)
views.py
#login_required(login_url='login')
def profilePage(request, pk): #www.mysite.com/profilePage/<pk>
user = User.objects.get(id=pk) #getting the user from <pk>
cards = MembershipCard.objects.filter(agent__manager=user.manager)
myFilter = MembershipCardFilter(request.GET,queryset=cards,user=user)
cards = myFilter.qs
#page_obj is used for Pagination, and contains the cards, i removed this part of code for better readability, can add it if needed
context = {'page_obj': page_obj,"user": user,"myFilter":myFilter}
return render(request, 'polls/profilePage.html',context)
filters.py
class MembershipCardFilter(django_filters.FilterSet):
class Meta:
model = MembershipCard
fields = ['agent','agent__subManager']
exclude = ['creation_date']
By reading answers to similar questions i think i have to modify the __init__ method in the CardFilter class, i've tried to adapt some answers to my case but it didn't work for some reasons . Any anser/comment is appreciated!
PS: I don't know if the title is clear, feel free to suggest a better one
You can try feeding the agent dropdown during init like (not tested!):
class MembershipCardFilter(django_filters.FilterSet):
agent= django_filters.ModelChoiceFilter(
queryset=Agent.objects.none(),
)
class Meta:
model = MembershipCard
fields = ['agent','agent__subManager']
exclude = ['creation_date']
def __init__(self, *args, **kwargs):
user = kwargs.get("user")
agents = Agent.objects.filter(manager__user=user)
super().__init__(*args, **kwargs)
self.filters["agent"].queryset = agents

Field 'id' expected a number but got <Listing: Ps4 Pro>

It's my first time creating a Django website with models, and in my first attempt to insert data into my table I'm getting this error.
My models are as follows:
class User(AbstractUser):
pass
#https://docs.djangoproject.com/en/3.1/topics/auth/default/
class Listing(models.Model):
listingID = models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="listID")
created_by = models.ForeignKey(User, on_delete=models.SET_NULL, related_name="myListing", null=True)
watchers = models.ManyToManyField(User, blank=True, related_name="watchlist")
title = models.CharField(max_length=30)
description = models.TextField()
creation_date = models.DateField(auto_now=True)
img_url = models.URLField()
active = models.BooleanField(default=True)
def __str__(self):
return f"{self.title}"
class Bid(models.Model):
listing = models.ForeignKey(Listing, on_delete=models.SET_NULL, related_name="bidsMadeOnMe", null=True, blank=True)
user = models.ForeignKey(User, on_delete=models.SET_NULL, related_name="myBids", null=True)
price = models.FloatField()
creation_date = models.DateField(auto_now=True, null=True)
def __str__(self):
return f"Bid={self.price}"
and the view that handles the form submission is this one:
#login_required
def create_listing(request):
if request.method == "POST":
user = User.objects.get(username=request.user.username)
l = Listing(created_by=user,
title=request.POST["title"],
description=request.POST["desc"],
# https://stackoverflow.com/questions/12176585/handling-dates-over-request-get
creation_date=models.DateTimeField(auto_now_add=True),
img_url=request.POST["image_url"]
)
l.save()
b = Bid(l,
user,
request.POST["initial_bid"],
models.DateTimeField(auto_now_add=True)
)
b.save()
return render(request, "auctions/index.html")
I know the problem is the way I'm adding the data but I can't fix it. Can someone give me some light?
Your problem (well, several actually) is this:
b = Bid(l, user, request.POST["initial_bid"], models.DateTimeField(auto_now_add=True))
You're constructing a model instance by positional arguments instead of keyword arguments. This can be done, but then the invisible "id" column that has been added to the Bid model, is the first argument.
In Django we never construct models like that, but always use keyword arguments, so we're not depending on field order:
b = Bid(listing=l, user=user, ...))
Once you're solved that, your next problem is the date field.
Don't assign fields to model instances. Fields are class declarations, they don't belong on instances. Fields describe on a class (= a Model), what kind data to expect. On the instance, you assign that data.
In this case, your definition for the field is wrong on the model and on the instance you shouldn't even assign it - it will be automatically filled.
Overall, it feels like you haven't gone through Django's tutorial or did not fully understand the concepts. I suggest you go through it.

Django M2M field in a Model Form?

I have a many to many field ConnectedTo in my model and I want to create the object using a form. However when I list it as a field I just get a listbox with options to highlight and no way of selecting one or more.
Ideally I'd love a multiple selection checkbox with a list of items in a scroll box. But I'd start with just having a selectable item.
Here's my code so far:
models.py:
class Part(models.Model):
PartID = models.AutoField(primary_key=True, unique=True)
SiteID = models.ForeignKey('Site', on_delete=models.CASCADE, null=True)
Comment = models.CharField(max_length=255, blank=True)
Subtype = models.ForeignKey('Subtype', on_delete=models.CASCADE, null=True)
Location = models.CharField(max_length=255, blank=True)
ConnectedTo= models.ManyToManyField('self', blank=True, null=True)
BatchNo = models.CharField(max_length=32, blank=False, null=True)
SerialNo = models.CharField(max_length=32,blank=True)
Manufacturer = models.CharField(max_length=32, blank=False, null=True)
Length = models.CharField(max_length=6, blank=True, null=True)
InspectionPeriod = models.IntegerField(blank=True, null=True)
LastInspected = models.DateField(blank=True, null=True)
InspectionDue = models.CharField(max_length=255, blank=True)
#classmethod
def create(cls, siteid, comment, subtype, location, batchno, serialno, manufacturer, length, inspectionperiod, lastinspected, inspectiondue):
part = cls(SiteID = siteid, Comment = comment, Subtype = subtype, Location = location, BatchNo = batchno, SerialNo = serialno, Manufacturer = manufacturer, Length = length, InspectionPeriod = inspectionperiod, LastInspected = lastinspected, InspectionDue = inspectiondue)
return part
def __str__(self):
return str(self.PartID)
forms.py:
class PartForm(forms.ModelForm):
class Meta:
model = Part
fields = ('Comment', 'Subtype', 'Location', 'ConnectedTo', 'BatchNo', 'SerialNo', 'Manufacturer', 'Length', 'InspectionPeriod', 'LastInspected')
views.py:
#login_required(login_url='/accounts/login/')
def addPartForm_Create(request, site, subtype):
siteselected = site
subtypeselected = Subtype.objects.get(SubtypeID = subtype)
if request.method == 'POST':
form = addPartForm(request.POST)
if form.is_valid():
obj = form.save(commit=False)
obj.SiteID = Site.objects.get(SiteID = siteselected)
obj.Subtype = subtypeselected
obj.save()
return redirect('/sites/'+str(site))
else:
form = addPartForm()
return render(request, 'myproj/addPart.html', {'form': form, 'SiteNo': Site.objects.get(SiteID = siteselected).SiteID, 'subtype': subtypeselected})
EDIT: had the wrong view, sorry.
EDIT 2: example of what I mean by the highlighted box:
UPDATE:
Jey_Jen's answer has helped me get the style I want. I now have a multiple selection checkbox. But the ConnectedTo attributes do not save. Everything else in the model is saved and a new part is created. But no many to many links.
I would suggest looking into django form widgets. you can override the default widget to be a whatever you want. you can view them here.
heres a small example the django docs give:
class CommentForm(forms.Form):
name = forms.CharField()
url = forms.URLField()
comment = forms.CharField(widget=forms.Textarea)

inserting data in two database at the same time

I have two Models in my application in Django
The Change model is only for storing the change logs made in Model Ip
My models.py
class Change(models.Model):
author = models.ForeignKey('auth.User', null=False, on_delete=models.CASCADE, default='auth.User')
ip = models.ForeignKey('Ip', on_delete=models.CASCADE, default='')
old_cluster = models.ForeignKey('Cluster', on_delete=models.CASCADE, default='')
old_status = models.ForeignKey('Status', on_delete=models.CASCADE, default='')
new_cluster = models.CharField(max_length=20)
new_status =models.CharField(max_length=20)
change_date = models.DateTimeField(default=timezone.now)
class Ip(models.Model):
author = models.ForeignKey('auth.User', null=False, on_delete=models.CASCADE, default='auth.User')
number = models.CharField(max_length=20, unique=True, default='')
status = models.ForeignKey('Status', on_delete=models.CASCADE, default='')
cluster = models.ForeignKey('Cluster', on_delete=models.CASCADE, default='')
created_date = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.number
class Meta:
ordering = ('number',)
my views.py
def ip_edit(request, id):
ip_edit = get_object_or_404(Ip, id=id)
form = EditManagementForm(request.POST, instance=ip_edit)
change_form = ChangeLogsForm()
if request.method == "POST":
if form.is_valid():
ip_edit = form.save(commit=False)
ip_edit.save()
change_form = ChangeLogsForm(request.POST)
if change_form.is_valid():
ip_change = change_form.save(commit=False)
ip_change.author = request.user
ip_change.ip = request.number
ip_change.save()
return redirect('/app/management')
else:
form = EditManagementForm(instance=ip_edit)
args = {
'form': form,
}
return render(request, 'app/ip_edit.html', args
and my forms.py
class EditManagementForm(forms.ModelForm):
class Meta:
model = Ip
fields = (
'number',
'status',
'cluster',
)
widgets = {
'number': TextInput(attrs={'class': 'ls-form-text'}),
}
class ChangeLogsForm(forms.ModelForm):
class Meta:
model = Change
fields = (
'ip',
'old_cluster',
'old_status',
'new_cluster',
'new_status',
)
when I save the ip information editing, no error occurs, but it also does not save the Model Change information
could you help me and let me know if there is any more correct and simple way to store a history of changes in a Model?
Since your Change model has a foreign key relation to Ip model and since ip_change in your view.py presents the instance of Change model then you should replace ip_change.ip = request.number with ip_change.ip = ip_edit because ip_edit is the instance of Ip model.
Do you want to perform the save operation on Ip and Change models only when both forms are valid? If you want that then this code has one really serious problem which people sometimes overlook and that problem is related to violation of database integrity. Just try to think about the situation when form.is_valid() returns True and change_form.is_valid() returns False. If that happens you will only save the data into Ip database table and nothing into Change database table because this line ip_change.save() will not be reached. If that happens the data integrity will be ruined and I guess you don't want that - you probably want to ensure that both saveoperations either executes or not. You should validate both forms at the same time like if form.is_valid() and change_form.is_valid() and put the rest of logic into that block.
P.S.
Since I started to talk about the violation of database integrity you should also have a look at atomic database transactions, it can be useful to know.

Categories

Resources