Django modelformset_factory() adding field manually in views - django

Disclaimer: I am new at Django so I'm sure my code is ugly.
Problem:
My current Model is built as follows:
class person(models.Model):
email = models.EmailField()
date = models.DateField()
phone_number = models.IntegerField()
name = models.CharField(max_length = 50)
belongs_to_group = models.ForeignKey(Group, related_name='group', on_delete=models.SET_NULL, null=True)
belongs_to_user = models.ForeignKey(User, related_name='user', on_delete=models.SET_NULL, null=True)
I have built a modelformset_factory for this using the following code:
personModelFormset = modelformset_factory(
person,
fields=('email', 'date' , 'phone_number', 'name'),
extra=1)
This makes the fields the form renders in the HTML email, date , phone_number, and name. This means that to successfully save the form to the database, I need to also add the fields belongs_to_group and belongs_to_user manually since the website user shouldn't be able to edit these (they should be automatically generated).
Attempted Solution:
To try to do this, I used the following view:
def classes(request):
#add form creation method here
user = request.user
group = value #taken from another form
if request.method == 'POST':
form_2 = personModelFormset (request.POST)
if form_2.is_valid():
for form in form_2:
form.belongs_to_group = value
form.belongs_to_user = user
form.save()
return redirect('home')
But this does not append the information to the form. This method works for me in a normal modelform, so I think I'm not using the modelformset_factory correctly. Does anyone know how I should correctly append the "behind the scenes" model fields to the formsetfactory? Thank you!

Related

Django ModelChoiceField Issue

I've got the following Situation, I have a rather large legacy model (which works nonetheless well) and need one of its fields as a distinct dropdown for one of my forms:
Legacy Table:
class SummaryView(models.Model):
...
Period = models.CharField(db_column='Period', max_length=10, blank=True, null=True)
...
def __str__(self):
return self.Period
class Meta:
managed = False # Created from a view. Don't remove.
db_table = 'MC_AUT_SummaryView'
Internal Model:
class BillCycle(models.Model):
...
Name = models.CharField(max_length=100, verbose_name='Name')
Period = models.CharField(max_length=10, null=True, blank=True)
Version = models.FloatField(verbose_name='Version', default=1.0)
Type = models.CharField(max_length=100, verbose_name='Type', choices=billcycle_type_choices)
Association = models.ForeignKey(BillCycleAssociation, on_delete=models.DO_NOTHING)
...
def __str__(self):
return self.Name
Since I don't want to connect them via a Foreign Key (as the SummaryView is not managed by Django) I tried a solution which I already used quite a few times. In my forms I create a ModelChoiceField which points to my Legacy Model:
class BillcycleModelForm(forms.ModelForm):
period_tmp = forms.ModelChoiceField(queryset=SummaryView.objects.values_list('Period', flat=True).distinct(),
required=False, label='Period')
....
class Meta:
model = BillCycle
fields = ['Name', 'Type', 'Association', 'period_tmp']
And in my view I try to over-write the Period Field from my internal Model with users form input:
def billcycle_create(request, template_name='XXX'):
form = BillcycleModelForm(request.POST or None)
data = request.POST.copy()
username = request.user
print("Data:")
print(data)
if form.is_valid():
initial_obj = form.save(commit=False)
initial_obj.ModifiedBy = username
initial_obj.Period = form.cleaned_data['period_tmp']
initial_obj.Status = 'Creating...'
print("initial object:")
print(initial_obj)
form.save()
....
So far so good:
Drop Down is rendered correctly
In my print Statement in the View ("data") I see that the desired infos are there:
'Type': ['Create/Delta'], 'Association': ['CP'], 'period_tmp': ['2019-12']
Still I get a Select a valid choice. That choice is not one of the available choices. Error in the forms. Any ideas??

Django forms(not ModelForms): pre-populate checkbox data from model onto forms

I am trying using django forms and not model forms. While trying to populate data for edit I keep getting keyError (u 'manager'). This does not appear if I remove assignment of a field 'choices'. Choice is a many to many field on my model. to make it less confusing I will paste my model, forms and view.
#model.py
class Choices(models.Model):
choices = models.CharField(max_length=70)
def __unicode__(self):
return self.choices
class UserProfile(models.Model):
user = models.OneToOneField(MyUser)
about_me = models.TextField(max_length=2000, null=True, blank=True)
country = models.CharField(max_length=100,null=True, blank=True)
choices = models.ManyToManyField(Choices, blank=True)
#forms.py
class UserProfileForm(forms.Form):
CHOICES = (
('like to cook', 'like to cook'),
('like only to eat', 'like only to eat')
)
about_me = forms.CharField(widget=forms.Textarea, required=False)
country = forms.CharField(max_length=60,required=False)
choices =forms.MultipleChoiceField(choices=CHOICES,widget=forms.CheckboxSelectMultiple(),required=False)
#views.py
#login_required
def update_profile(request):
userprofile = UserProfile.objects.get(user=request.user)
form = UserProfileForm(initial={'about_me':userprofile.about_me,
'country':userprofile.country,
'choices': userprofile.choices})
return render(request, 'u_profiles/edit_pro.html', {'form':form})
now when I assign the initial value of selected choices I get the keyerror. I would like to know the correct way of doing this.
Thanks.

How to use a submit a one-to-many form

I have a list of employees who work at a site. Each site is owned by a User (using Django's standard user model).
I want to create a form that adds an employee and automatically links them to a site dependent on who the authenticated user is:
models.py:
class Employee(models.Model):
site = models.ForeignKey(Site, null=True)
employee_name = models.CharField(default='name', max_length=128, blank=False, null=False)
class Site(models.Model):
user = models.ForeignKey(User)
site_name = models.CharField(max_length=128, blank=False, null=False)
views.py:
site_profile = Site.objects.get(user=request.user)
if request.method == "POST":
form = EmployeeAddForm( request.POST )
if form.is_valid():
obj = form.save(commit=False)
obj.site = site_profile
obj.save()
return redirect('dashboard_home')
form = EmployeeAddForm()
return render(request, "dashboard/employees.html", {'form': form })
forms.py:
class EmployeeAddForm(forms.ModelForm):
class Meta:
model = Employee
fields = ( 'employee_name')
This code will add the employee to the database, but in django admin, list_display = 'site' results in Site object not the actual site name. It does not appear that the employee is linked to the site.
If I use obj.site = site_profile.id (adding .id), I get the error Cannot assign "1": "Employee.site" must be a "Site" instance.
Found the error: the above code is correct, I simply had a tab ordering error in my Site modeL
class Site(models.Model):
...
def __str__(self):
return self.site_name
def should have been inserted 1 tab inwards.

Why does my form in Django save successfully except for one field?

my form in forms.py is then passed to this method in my views.py, if I go into python shell and print objects from MyProfile, all of the fields show values except for nearbyzips, which shows None. As you can see below, I am trying to manually assign a value to nearbyzips when the form is saved.
inside views.py
#secure_required
#login_required
def profile_edit(request, username, edit_profile_form=EditProfileForm,
template_name='userena/profile_form.html', success_url=None,
extra_context=None, **kwargs):
profile = get_profile(user)
form = edit_profile_form(instance=profile, initial=user_initial)
if request.method == 'POST':
if form.is_valid()
cleanzipcode = form.cleaned_data['zipcode']
nearestzips = PostalCode.objects.distance(PostalCode.objects.get(code=cleanzipcode).location)
zip_codes = list(nearestzips.values_list('code', flat=True))
//print zip_codes
form.cleaned_data['nearbyzips'] = zip_codes
//print form.cleaned_data['nearbyzips']
profile=form.save()
return redirect(redirect_to)
models.py
class MyProfile(UserenaBaseProfile):
user = models.OneToOneField(User,
unique=True,
verbose_name=_('user'),
related_name='my_profile')
streetaddress=models.CharField(null=True, blank=True, max_length=30)
city = models.CharField(null=True, blank=True, max_length=20)
state = models.CharField(null=True, blank=True, max_length=20)
zipcode = models.IntegerField(_('zipcode'),
max_length=5, null=True, blank=True)
nearbyzips = models.IntegerField(null=True, blank=True, max_length=100)
phone=models.CharField(null=True, blank=True, max_length=16)
websiteurl=models.CharField(null=True, blank=True, max_length=38)
Something to keep in mind, if I go into python shell and run:
nearestzips = PostalCode.objects.distance(PostalCode.objects.get(code='97202').location
print nearestzips
It prints all the Postal Codes I would expect. So I'm not sure where exactly is broken. I don't see any errors in my logs.
UPDATE:
I have added print statements in my views. printing zip_codes and form.cleaned_data['nearbyzips'] both show:
[u'97202', u'97206', u'97214', u'97215', u'97239']
But it still does not appear to be saving to the form.
2 things stand out to me here.
Your form is created for some kind of profile model (get_profile_model()) -- does this profile model have a field called nearbyzips?
If your model does have a field called nearbyzips, explicitly include it (and all the fields you want to update) in a tuple/list of fields in your form class's inner Meta class.
Also, I don't see you calling the save method on your form class in your view function (i.e. form.save()).
Change this line:
tzips = PostalCode.objects.distance(PostalCode.objects.get(code='cleanzipcode').location)
to this:
tzips = PostalCode.objects.distance(PostalCode.objects.get(code=cleanzipcode).location)

Django - having trouble saving filtered object

For my first Django project I'm trying to make an app that lets users create lists of media (books, movies, etc.) with various fields describing each object (title, author, etc.), and I'm having trouble figuring out how to get it to save. That is to say that nothing happens when the form is submitted. Can someone tell me what I'm doing wrong? Sorry if this is a bit of a noob question; it seems like I'm missing something really basic here. (I'm using basic HTML forms instead of ModelForms because for some media types I want to ignore certain fields - e.g. "author" for movies - but if there is an easy way to do that using ModelForms, I'm all ears.)
from views.py:
def editbook(request,list_owner,pk):
book_list = Item.objects.all().filter(item_creator=list_owner).filter(category='book').order_by('type','name')
item_to_edit = Item.objects.get(pk=pk)
if request.method == 'POST':
item_to_edit.save()
return render_to_response('books.html', {'booklist': book_list, 'listowner': list_owner}, RequestContext(request))
else:
form=EditItem()
return render_to_response('editbook.html', {'listowner': list_owner, 'item_to_edit': item_to_edit}, RequestContext(request))
from models.py:
CATEGORY_CHOICES = (
('book','book'),
('tv','tv'),
('movie','movie'),
('game','game'),
('music','music'),
)
class Item(models.Model):
item_creator = models.CharField(max_length=30) # user name goes here
category = models.CharField(max_length=5, choices=CATEGORY_CHOICES)
name = models.CharField(max_length=70)
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
artist = models.CharField(max_length=70, blank=True)
type = models.CharField(max_length=50, blank=True)
progress = models.CharField(max_length=10, blank=True)
finished = models.BooleanField(default=False)
rating = models.IntegerField(default=0, blank=True, null=True)
comment = models.CharField(max_length=140, blank=True)
def __unicode__(self):
return self.name
There is, of course, a way to only use some fields in a modelform: as fully documented in Using a subset of fields on the form, you can use the fields or exclude attributes in the form's Meta class.
However you'll still need, as szaman points out, to pass the POST data to the form and check for validity, and in addition you'll need to pass in the instance paramater as you're updating an existing instance.
What I see is that you get object from database and when form is submitted than just saving the object, but you don't update any field so you cannot see changes in db. Try to do:
if request.method == "POST":
form = MyForm(request.POST)
logging.info("form.is_valid() %s" % form.is_valid())
if form.is_valid():
item_to_edit.name = form.cleaned_data['name']
item_to_edit.save()
...