Django multi model form, save or update on one model - python

I have 2 models in 1 form. in this form data can be changed.
For 1 model (Sloep_core) the content only have to update, this is working.
for the other model (Sloep_gegevens) the content normaly will update.
Only if the value 'sloepnaam' changed in the form for model 'Sloep_gegevens', the record for the model 'Sloep_gegevens' must enter a new record and not update the old one.
The save() must change from update to insert, but how??
I have tried to put soms code together (with some search actions). I come to the code below but now it is always a insert the code. Is someone have a idea to get it right?
model.py:
class Sloep_core(models.Model):
FSN_nummer = models.IntegerField(unique=True)
HT_nummer = models.IntegerField(unique=True, blank=True, null=True)
SRNL_nummer = models.IntegerField(blank=True, null=True)
sloep_type = models.CharField(max_length=128, blank=True)
werf = models.CharField(max_length=255, blank=True)
info = models.TextField(blank=True)
objects = SloepManager()
def __str__(self):
return str(self.FSN_nummer)
class Sloep_gegevens(models.Model):
sloep = models.ForeignKey(Sloep_core, on_delete=models.CASCADE, default='')
sloepnaam = models.CharField(max_length=128)
thuishaven = models.CharField(max_length=128, blank=True)
date = models.DateField(auto_now=True)
__original_sloepnaam = None
def __init__(self, *args, **kwargs):
super(Sloep_gegevens, self).__init__(*args, **kwargs)
self.__original_sloepnaam = self.sloepnaam
def save(self, force_insert=False, force_update=False, *args, **kwargs):
if self.sloepnaam != self.__original_sloepnaam:
# then do this
force_insert = True
else:
# do that
force_update = True
super(Sloep_gegevens, self).save(force_insert, force_update, *args, **kwargs)
self.__original_sloepnaam = self.sloepnaam
class Meta:
ordering = ["date"]
def __str__(self):
return self.sloepnaam
form.py:
class SloepGegevensForm(forms.ModelForm):
class Meta:
model = Sloep_gegevens
exclude = ['pk', 'sloep']
class SloepCoreForm(forms.ModelForm):
class Meta:
model = Sloep_core
exclude = ['pk', 'SRNL_nummer']
views.py:
def sloep_edit(request, pk):
sloep = Sloep_core.objects.get(pk=pk)
sloep_sg = Sloep_gegevens.objects.filter(sloep=pk).order_by('-date')[0]
if request.method == "POST":
formSG = SloepGegevensForm(request.POST)
formSC = SloepCoreForm(request.POST, instance=sloep)
if all([formSG.is_valid(), formSC.is_valid()]):
sloep = formSC.save()
SG = formSG.save(commit=False)
SG.sloep = sloep
SG.save()
return redirect('sloep_detail', pk=sloep.pk)
else:
formSG = SloepGegevensForm(instance=sloep_sg)
formSC = SloepCoreForm(instance=sloep)
return render(
request,
'sloepen/sloep_edit.html',
{'formSG': formSG, 'formSC': formSC,})

I found the answer for my problem. In the view I didn't give a pk with the SloepGegevensForm in the POST request.model.
I changed: formSG = SloepGegevensForm(request.POST)into formSG = SloepGegevensForm(request.POST, instance=sloep_sg)
This makes the save() working.

Related

How to display form in django

I'am little confiused because my code in django does't work and I don't know why. I want to create a form displaying in html file. When I click on thh button, the url have to redirect me in the html file where I've put the form code. But the django return me a error
'User' object has no attribute 'nazwa_set'
My models.py is:
from django.db import models
from django.contrib.auth.models import User
class Firma(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="Użytkownik")
nazwa = models.CharField(max_length=250,verbose_name='Nazwa firmy', unique=False)
class Meta:
verbose_name = 'Firmę'
verbose_name_plural = 'Firmy'
def __str__(self):
return self.nazwa
class Cudzoziemiec(models.Model):
OBYWATELSTWA = (
('RU', 'Rosja'),
('UA', 'Ukraina'),
('BY', 'Białoruś'),
)
TYTUL_POBYTOWY = (
('WZ', 'Wiza'),
('KP', 'Karta pobytu')
)
user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="Użytkownik")
nazwa = models.ForeignKey(Firma, on_delete=models.CASCADE, verbose_name="Firma")
obywatelstwo = models.CharField(max_length=250,choices=OBYWATELSTWA, verbose_name="Obywatelstwo")
imie = models.CharField(max_length=80, verbose_name="Imię", unique=False)
nazwisko = models.CharField(max_length=150, verbose_name="Nazwisko", unique=False)
data_ur = models.DateField(auto_now=False, auto_now_add=False, verbose_name="Data urodzenia")
miejsce_ur = models.CharField(max_length=100, verbose_name="Miejsce urodzenia")
paszport = models.CharField(max_length=30, verbose_name="Paszport")
data_start_pasz = models.DateField(auto_now=False, auto_now_add=False, verbose_name="Data wydania paszportu")
data_koniec_pasz = models.DateField(auto_now=False, auto_now_add=False, verbose_name="Data ważności paszportu")
dok_pobytowy = models.CharField(max_length=250,choices=TYTUL_POBYTOWY, verbose_name="Tytuł pobytowy")
data_start_pobyt = models.DateField(auto_now=False, auto_now_add=False, verbose_name="Dokument pobytowy ważny od")
data_koniec_pobyt = models.DateField(auto_now=False, auto_now_add=False, verbose_name="Dokument pobytowy ważny do")
class Meta:
verbose_name = 'Cudzoziemca'
verbose_name_plural = 'Cudzoziemcy'
def __str__(self):
return f'{self.imie} {self.nazwisko}'
in the view.py responsible def for adding the new record:
#login_required
def nowy_pracownik(request):
if request.method == "POST":
nowy_pracownik = CudzoziemiecForm(request.user, request.POST)
if nowy_pracownik.is_valid():
nowy_pracownik.save()
messages.success(request, 'Pomyślnie dodano pracownika !')
return render(request, 'cudzoziemiec/nowy_pracownik_ok.html')
else:
nowy_pracownik = CudzoziemiecForm(request.user)
return render(request, 'cudzoziemiec/nowy_pracownik.html', {'nowy_pracownik':nowy_pracownik})
And on the end here is my forms.py :
class FirmaForm(forms.ModelForm):
class Meta:
model = Firma
fields = ('nazwa',)
class CudzoziemiecForm(forms.ModelForm):
class Meta:
model = Cudzoziemiec
fields = ('nazwa','imie', 'nazwisko','obywatelstwo', 'data_ur','paszport', 'data_start_pasz', 'data_koniec_pasz', 'dok_pobytowy', 'data_start_pobyt', 'data_koniec_pobyt')
def __init__(self, user, *args, **kwargs):
super(CudzoziemiecForm, self).__init__(*args, **kwargs)
self.fields['nazwa'].queryset = user.nazwa_set.all()
self.user = user
def save(self, commit=True):
instance = super(CudzoziemiecForm, self).save(commit=False)
instance.user = self.user
if commit:
instance.save()
return instance
The error is probably somewhere in the forms. py in the class CudzoziemiecForm in line self.fields['nazwa'].queryset = user.nazwa_set.all()
In django default reverse lookup name is modelname_set. So when you trying to get user.nazwa_set.all() this means that there is some model Nazwa related with User. Since in your code you don't have model named Nazwa this line raise the error. I suppose you mean Cudzoziemiec or Firma so to fix problem you need to replace user.nazwa_set.all() with user.firma_set.all() in form's __init__ method.

Modify models field via view doesn't work

In my Django application, I want to subtract 1 "free_places" field in the "Event" model using the "EventDetailView" view where the form is located. Each time the form is OK (when the user subscribes to the event), the "free_places" field should decrease by 1.
I do not know why my code does not work.
My view:
class EventDetailView(DetailView, ModelFormMixin):
model = models.Event
form_class = forms.RegisterForm
context_object_name = 'event'
def get_success_url(self):
return reverse('events:list')
def get_initial(self):
return {'event': self.kwargs['pk']}
def post(self, request, *args, **kwargs):
form = self.get_form()
if form.is_valid():
self.object = self.get_object()
self.object.free_places - 1
self.object.save()
return self.form_valid(form)
else:
return self.form_invalid(form)
Models:
class Event(models.Model):
title = models.CharField(max_length=500)
date = models.DateField()
text = models.TextField()
image = FilerImageField(null=True, blank=True)
flag = models.ForeignKey(Flag)
free_places = models.IntegerField()
class Meta:
ordering = ['-date']
def __str__(self):
return self.title
#property
def slug(self):
return slugify(self.title)
def get_absolute_url(self):
return reverse('events:detail', args=[self.slug, self.id])
def get_model_id(self):
return self.id
class Register(models.Model):
event = models.ForeignKey(Event)
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
company = models.CharField(max_length=30, blank=True)
street = models.CharField(max_length=50, blank=True)
post_code = models.CharField(max_length=30, blank=True)
city = models.CharField(max_length=30, blank=True)
email = models.EmailField()
phone_number = models.IntegerField()
def __str__(self):
return self.first_name
def get_event_name(self):
return self.event
You need to assign the result of self.object.free_places - 1. At the moment you are not doing anything with it.
Change the line to either:
self.object.free_places -= 1
or
self.object.free_places = self.object.free_places - 1
The code is vulnerable to race conditions if multiple users submit the form at the same time. You can fix that by using F() objects.
from django.db.models import F
self.object.free_places = F('free_places') - 1
self.object.save()

How do I save the foreign key?

I want to upload multiple images.
class IssuePanel(models.Model):
issue = models.ForeignKey(ComicIssue, on_delete=models.CASCADE)
panel = models.FileField(upload_to='comic_issues_files/panels/')
date_uploaded = models.DateTimeField(auto_now_add=True)
After following the examples on django-multiupload's repository on github, I have this on forms.py
class PanelsForm(forms.ModelForm):
class Meta:
model = ComicIssue
fields = ('issue', 'issue_title', 'issue_cover', 'issue_description', 'issue_cover', 'issue_file')
panels = MultiFileField(min_num=1, max_num=20, max_file_size=2048*2048*5)
def save(self, commit=False):
instance = super(PanelsForm, self).save()
for each in self.cleaned_data['panels']:
IssuePanel.objects.create(panel=each, issue=instance)
return instance
views.py
class ComicIssueCreate(LoginRequiredMixin, CreateView):
model = ComicIssue
slug_field = 'comicseries_id'
form_class = PanelsForm
def form_valid(self, form):
obj = form.save(commit=False)
obj.title = ComicSeries.objects.get(id=self.kwargs['pk'])
obj.user = self.request.user
obj.save()
return redirect('comics:series_detail', pk=obj.title.id, slug=obj.title.slug)
urls.py
url(r'^comic/issue/(?P<pk>[0-9]+)/add/$', views.ComicIssueCreate.as_view(), name='comic-issue-add'),
However, I get this error
IntegrityError at /comic/issue/21/add/
NOT NULL constraint failed: comics_comicissue.title_id
class ComicIssue(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE,
null=True, blank=True, verbose_name='Uploaded by: '
)
title = models.ForeignKey(ComicSeries, on_delete=models.CASCADE, verbose_name='Series Title')
issue = models.CharField(verbose_name='Issue Number', max_length=500)
issue_title = models.CharField(verbose_name='Issue Title', max_length=1000)
issue_cover = models.ImageField(verbose_name='Issue cover', upload_to='comic_issues', height_field=None, width_field=None, max_length=None)
issue_description = models.TextField(verbose_name='Description')
issue_file = models.FileField(verbose_name='Issue file', upload_to='comic_issues_files', max_length=100,
help_text='File in pdf or as single image', null=True, blank=True
)
date_added = models.DateTimeField(auto_now_add=True, null=True)
is_favorite = models.BooleanField(default=False)
issue_slug = models.SlugField(default='')
class Meta:
verbose_name = 'Comic Issue'
verbose_name_plural = 'Comic Issues'
def __str__(self):
return '{}: {} issue number - {}'.format(self.title.title, self.issue_title, self.issue)
def save(self, *args, **kwargs):
self.issue_slug = slugify(self.issue_title)
super(ComicIssue, self).save(*args, **kwargs)
def get_absolute_url(self):
return reverse('comics:issue_detail', kwargs={'issue_slug':self.issue_slug,'pk': self.pk})
Could this function in the ComicIssue model be a problem since it is also highlighted on the error page:
def save(self, commit=False, *args, **kwargs):
self.issue_slug = slugify(self.issue_title)
super(ComicIssue, self).save(*args, **kwargs)
I am passing the title_id from the url. It is working on other models just not this one. How do I save the foreign key?
You're saving your form in the form's save method regardless of the commit value. The first line is instance = super(PanelsForm, self).save() which will try to save a ComicIssue instance even though in your view, you wrote obj = form.save(commit=False).
You can do two things: pass the title and user to your form at init so the form can handle assigning those during save. Or change your form's save method to:
def save(self, commit=False):
instance = super(PanelsForm, self).save(commit=commit)
if commit:
for each in self.cleaned_data['panels']:
IssuePanel.objects.create(panel=each, issue=instance)
return instance
And then your view needs to call the form's save() method twice (note that the form's instance is passed by reference, so changing it in the view also changes the form's instance):
def form_valid(self, form):
obj = form.save(commit=False)
obj.title = ComicSeries.objects.get(id=self.kwargs['pk'])
obj.user = self.request.user
form.save()
return redirect('comics:series_detail', pk=obj.title.id, slug=obj.title.slug)

My data population no longer works after using Django's User model. Why?

This code works perfectly, which is a User model with one to many relationship with UserRating model.
View:
def index(request):
user_list = User.objects.order_by('-userrating')[:5]
city_list = City.objects.order_by('-name')[:5]
context_dict = {"users": user_list, "cities" : city_list}
return render(request, "index.html", context_dict)
Models:
# this is model for user
class User(models.Model):
username = models.CharField(max_length=128, unique=True)
email = models.EmailField(max_length=128, unique=True)
profilepic = models.ImageField(null=True)
firstname = models.CharField(max_length=128, null=True)
secondname = models.CharField(max_length=128, null=True)
city = models.ForeignKey(City)
slug = models.SlugField(unique=True)
def save(self, *args, **kwargs):
# Uncomment if you don't want the slug to change every time the name changes
# if self.id is None:
# self.slug = slugify(self.name)
self.slug = slugify(self.username)
super(User, self).save(*args, **kwargs)
def __unicode__(self):
return self.username
#property
def avg_rating(self):
return self.userrating_set.all().aggregate(Avg('rating'))['rating__avg']
# this is the model for user ratings - one to many relationship with User
class UserRating(models.Model):
user = models.ForeignKey(User)
comment = models.CharField(max_length=500)
for_username = models.CharField(max_length=128)
rating = models.IntegerField(default=5)
def __unicode__(self):
return unicode(self.rating)
However, it breaks once I use Django's built in User model as below (User model with one to one relationship with a UserProfile model and a one to many relationship with UserRating)
# this is model for user
class UserProfile(models.Model):
user = models.OneToOneField(User)
profilepic = models.ImageField(blank=True)
city = models.ForeignKey(City)
slug = models.SlugField(unique=True)
def save(self, *args, **kwargs):
self.slug = slugify(self.user.username)
super(User, self).save(*args, **kwargs)
def __unicode__(self):
return self.user.username
#property
def avg_rating(User):
return self.userrating_set.all().aggregate(Avg('rating'))['rating__avg']
# this is the model for user ratings - one to many relationship with User
class UserRating(models.Model):
user = models.ForeignKey(User)
comment = models.CharField(max_length=500)
for_username = models.CharField(max_length=128)
rating = models.IntegerField(default=5)
def __unicode__(self):
return unicode(self.rating)
it generates this error when adding a user profile via admin page:
super(type, obj): obj must be an instance or subtype of type
You must call the actual superclass in your super call; it is UserProfile, not User.
def save(self, *args, **kwargs):
self.slug = slugify(self.user.username)
super(UserProfile, self).save(*args, **kwargs)

Automatically create a record in another model after using CreateView

I have two models (OK 3 models since AssignedAsset is a subclass of Asset), one that tracks assets and another that tracks the history of owners for that asset. When I create a new asset using CreatView I would like to automatically have it create a History record as well.
models.py
class Asset(models.Model):
make = models.CharField(max_length=100)
model = models.CharField(max_length=100)
serial_number = models.CharField(max_length=100)
po = models.ForeignKey('purchaseorders.PurchaseOrder', default=None, blank=True, null=True)
location = models.ForeignKey('locations.Plant')
slug = models.SlugField(blank=True, unique=True)
def __str__(self):
return self.slug
def save(self):
forslug = "{0.make}-{0.model}-{0.serial_number}".format(self)
self.slug = slugify(forslug)
super(Asset, self).save()
class AssignedAsset(Asset):
user = models.ForeignKey(User)
def __str__(self):
return self.slug
class AssignedHistory(models.Model):
assset = models.ForeignKey('Asset')
user = models.ForeignKey(User)
date = models.DateField()
slug = models.SlugField(blank=True, unique=True)
def __str__(self):
return self.slug
def save(self):
forslug = "{0.asset}-{0.date}".format(self)
self.slug = slugify(forslug)
super(AssignedHistory, self).save()
Here is my view.
class NewAssignedAsset(CreateView):
form_class = AssignedAssetForm
template_name = 'createassignedasset.html'
success_url = '/assets'
And my forms.py
class AssignedAssetForm(forms.ModelForm):
class Meta:
model = AssignedAsset
fields = ['make', 'model', 'serial_number', 'location', 'user', 'po']
def __init__(self, *args, **kwargs):
super(AssignedAssetForm, self).__init__(*args, **kwargs)
#Filter out PO's that have packingslips (otherwise you will quickly have a ridicously big drop-down of every PO in the system)
self.fields['po'] = forms.ModelChoiceField(required=False, queryset=PurchaseOrder.objects.filter(packing_slip=''))
I thought maybe I could have it create the history when it gets the success URL, so I tried this in my view:
import time
def today():
return time.strftime ("%m/%d/%Y")
class NewAssignedAsset(CreateView):
form_class = AssignedAssetForm
template_name = 'createassignedasset.html'
def get_success_url(self):
history = AssignedHistory.objects.create(assset=self.object, user=self.object.user, date=today())
return '/assets'
But this throws a TypeError:
save() got an unexpected keyword argument 'force_insert'
Anything that would point me in the right direction would be appreciated.
You can do it at multiple levels(DB level, form level).
In your case, I'll say you just need to override the save() of your AssignedAssetForm. (Assuming you set user in context of form)
def save(self, *args, **kwargs):
assigned_asset = super(AssignedAssetForm, self).save(*args, **kwargs)
user = self.context.get(u'user')
if user:
assigned_asset_history = AssignedHistory(asset=assigned_asset, user=user, date=datetime.date.today())
assigned_asset_history.save()
return assigned_asset
** I am not sure about the context part, you may have to look into how to use user in form.
You should write your Asset.save() and AssignedHistory.save() as:
def save(self, **kwargs):
...
super(YourModel, self).save(**kwargs)
...
Note the **kwargs. They allow you to accept optional parameters (and a Model.save() has a few).

Categories