ModelForm instance doesn't store primary key - python

Upon trying to save a Model instance as a result of saving a ModelForm, I'm getting the following error:
ValueError: "<Question: NameError: 'x' is not defined>" needs to have
a value for field "id" before this many-to-many relationship can be used.
The question instance is being created to an extent, yet the primary key is not being generated. What needs to be fixed in order for the primary key to be attached so it can be stored in the database?
class PostQuestionPage(QuestionPage):
template_name="questions/create_question.html"
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
context['question_form'] = QuestionForm(self.request.POST or None)
con
return context
def get(self, request):
context = self.get_context_data()
return self.render_to_response(context)
def post(self, request):
context = self.get_context_data()
form = context['question_form']
import pdb; pdb.set_trace()
if form.is_valid():
question = form.save(commit=False)
question.user_account = request.user.account
question.save()
form.save_m2m()
return HttpResponseRedirect(
reverse("questions:question", kwargs={'id': question.id})
)
return self.render_to_response(context)
class Question(models.Model):
title = models.CharField(max_length=50)
body = models.TextField()
dated = models.DateField(default=date.today)
likes = models.IntegerField(default=0)
user_account = models.ForeignKey(
'users.UserAccount',
on_delete=models.SET_NULL,
null=True, blank=True,
related_name="questions"
)
tags = models.ManyToManyField(Tag, related_name='questions')
objects = models.Manager()
dateranges = DateRangeQuerySet.as_manager()
status = QuestionStatusQuerySet.as_manager()
class Meta:
ordering = ['-dated']
default_manager_name = "objects"
def __str__(self):
return self.title
class TestPostQuestionPageAdded(TestCase):
'''Verify that a User has succesfully added a question'''
#classmethod
def setUpTestData(cls):
cls.user = User.objects.create_user("Me")
cls.account = UserAccount.objects.create(user=cls.user)
tag1 = Tag.objects.create(name="tag1")
tag2 = Tag.objects.create(name="tag2")
cls.data = mock_questions_submitted[1]
cls.data.update(
{'tags': [tag1, tag2]}
)
def test_user_submitted_question_added(self):
total_questions = Question.objects.count()
self.client.force_login(self.user)
response = self.client.post(
reverse("questions:create"),
data=self.data,
)
question = Question.objects.all().get()
self.assertEqual(response.status_code, 302)
self.assertEqual(Question.objects.count(), 1)
self.assertEqual(question.user_account, self.account)
{'_state': <django.db.models.base.ModelState object at 0x0000024F9A49D788>, 'id': None,
'title': 'What is a primary key?',
'body': "<p>Upon studying database relationships, a relation happens to be set of records\nof the same type that is stored in the database. Yet, I'm not understanding\nwhat purpose the primary key serves? How does a record benefit from having\na primary key?</p>",
'dated': datetime.date(2021, 4, 26),
'likes': 0, 'user_account_id': 1}
(Pdb) question.tags
*** ValueError: "<Question: What is a primary key?>"
needs to have a value for field "id" before this many-to-many relationship can be used.

Related

django.db.utils.IntegrityError: NOT NULL constraint failed: user.id

I don't know why the error is caused even though I designated the author.
I'd appreciate your help.
model & form
class SuperTitle(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='debate_author')
super_title = models.CharField(max_length=100)
liker = models.ManyToManyField(User, related_name='debate_liker')
def __str__(self):
return self.super_title
class SuptitForm(forms.ModelForm):
class Meta:
model = SuperTitle
fields = ['super_title']
views.py
def create(request):
...
dic = {'super_title' : request.POST.get('sup_title')}
sup_form = SuptitForm(dic)
if sup_form.is_valid():
sup_form.author = request.user
sup_form.super_title = ...
sup_form.save()
...
return
IntegrityError at /polls/debate/create/
NOT NULL constraint failed: polls_supertitle.author_id
You get this error becouse you want to save form(create new SuperTitle objetd in database) without author.
You need to pass author objectd or id somehow to form. If the request.user is author i recommend form:
class SuptitForm(forms.ModelForm):
class Meta:
model = SuperTitle
fields = ['super_title', 'author']
widgets = {'author': forms.HiddenInput()}
and in view:
def create(request):
if request.method == 'POST':
sup_form = SuptitForm(request.POST)
if sup_form.is_valid():
sup_form.save()
else:
sup_form = SuptitForm(initial={'author': request.user}
...

Django - NOT NULL constraint failed

I'm currently working on a Django app that will parse the contents of an uploaded log file to the associated database in my Django project. I've managed to get it all running as expected except it won't associate my uploaded data with the model's ForeignKey. I can assign null=True which resolves the integrity error but then of course, it doesn't assign any of the uploaded data to that ForeignKey. Here's the code:
models.py
class Case(models.Model):
case_ref = models.CharField(max_length=8)
oic = models.CharField(max_length=50)
subject = models.CharField(max_length=100)
submitted_date = models.DateTimeField(default=datetime.now, blank=True)
def get_absolute_url(self):
return reverse('case_list', kwargs={'pk': self.pk})
def __str__(self):
return self.case_ref + " " + self.subject
class TeamviewerLogs(models.Model):
case = models.ForeignKey(Case, on_delete=models.DO_NOTHING)
teamviewer_id = models.IntegerField()
teamviewer_name = models.TextField()
connection_start = models.TextField()
connection_end = models.TextField()
local_user = models.TextField()
connection_type = models.TextField()
unique_id = models.TextField()
def get_absolute_url(self):
return reverse('case_list', kwargs={'pk': self.pk})
def __str__(self):
return str(self.teamviewer_id) + " - " + str(self.teamviewer_id)
forms.py
class UploadLog(forms.ModelForm):
file = forms.FileField()
class Meta:
model = TeamviewerLogs
fields = [
'file'
]
views.py
def add_logs(request, pk):
case = get_object_or_404(Case, pk=pk)
if request.method == 'POST':
form = UploadLog(request.POST, request.FILES)
if form.is_valid():
teamviewer = form.save(commit=False)
teamviewer.case = case
log_file = request.FILES['file']
log_file = filter(None, (line.rstrip() for line in log_file))
for lines in log_file:
split = lines.decode('utf-8').split('\t')
teamviewer_id = split[0]
teamviewer_name = split[1]
connection_start = split[2]
connection_end = split[3]
local_user = split[4]
connection_type = split[5]
unique_id = split[6]
teamviewer = TeamviewerLogs(teamviewer_id=teamviewer_id, teamviewer_name=teamviewer_name,
connection_start=connection_start, connection_end=connection_end,
local_user=local_user, connection_type=connection_type, unique_id=unique_id)
teamviewer.save()
return redirect('tv_log_details', pk=case.pk)
form.save()
else:
form = UploadLog()
return render(request, 'teamviewer/add_logs.html', {'form': form})
But when I click to upload the file I'm hit with:
When it tries to execute teamviewer.save().
I've been trying to resolve this issue for hours and have tried so many different variations of answers from Stackoverflow or previous code I've used that has worked for different models but I've hit a brick wall...hard!
Any help anyone can offer would be greatly appreciated.
Ok, so here's an example of the concept I've suggested in the comments.
I've got a view which passes some data to the a form;
class ListingDetailView(DetailView):
""" Listing detail page """
model = Listing
template_name = 'listing.html'
def get_form_kwargs(self):
"""Return the kwargs for the form"""
kwargs = {}
initial = {
'listing': self.object,
}
kwargs['initial'] = initial
return kwargs
def get_form(self):
form = ApplicationSignupForm(
**self.get_form_kwargs()
)
return form
def get_context_data(self, **kwargs):
""" Add our form to the context """
context = super().get_context_data(**kwargs)
context['form'] = self.get_form()
return context
The form then makes use of that initial data and sets the field it relates to as hidden. I don't validate this data, but I'll try to show how you might do that;
class ApplicationSignupForm(forms.ModelForm):
class Meta:
""" Setup the form """
fields = (
'listing',
...
)
model = Application
widgets = {
'listing': forms.HiddenInput()
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
initial_data = kwargs['initial']
self.listing = initial_data.get('listing')
def clean(self):
"""
Custom form cleaning
"""
cleaned_data = super().clean()
listing = cleaned_data.get('listing')
if listing != self.listing:
self.add_error('listing', "You can't modify this value")
return cleaned_data

How to change ForeignKey value using inlineformset?

I have two models and forms linked by the ForeignKey 'squad'. In my templates I have the user first typing the Squad name and then the shooters. I am trying to hardcode the 'squad' field of my Shooters with the 'squad_name' of my ShooterSquad so the user doesn't have to type the squad name every time for every shooter.
models.py
class ShooterSquad(models.Model):
squad_name = models.CharField(unique=True, max_length=100)
school = models.CharField(max_length=100, null=False)
def __str__(self):
return self.squad_name
class Shooter(models.Model):
name = models.CharField(max_length=100)
squad = models.ForeignKey(ShooterSquad, to_field='squad_name', related_name='squad', on_delete=models.PROTECT)
def __str__(self):
return self.name
forms.py
class ShooterSquadForm(forms.ModelForm):
class Meta:
model = ShooterSquad
fields = ['squad_name', 'squad_total_score', ]
class ShooterForm(forms.ModelForm):
class Meta:
model = Shooter
fields = '__all__'
class BaseShooterFormSet(BaseModelFormSet):
def __init__(self, *args, **kwargs):
super(BaseShooterFormSet, self).__init__(*args, **kwargs)
self.queryset = Shooter.objects.none()
ShooterFormSet = inlineformset_factory(
ShooterSquad, Shooter,
form=ShooterForm,
formset=BaseShooterFormSet,
extra=1,
max_num=3,
exclude=('squad',)
)
views.py
def add_multiple_shooters(request):
if request.method == 'POST':
squad_form = ShooterSquadForm(request.POST)
formset = ShooterFormSet(request.POST)
if squad_form.is_valid() and formset.is_valid():
set_squad = squad_form.cleaned_data.get('squad_name')
for f in formset.forms:
f.cleaned_data['squad_id'] = set_squad
f.cleaned_data['squad'] = set_squad
print(formset.cleaned_data)
squad_form.save()
formset.save()
return redirect('anasp:mainpage')
else:
print("ERROR")
formset = ShooterFormSet()
squad_form = ShooterSquadForm()
context = {
"title": title,
"formset": formset,
"squad_form": squad_form,
}
return render(request, "anasp/scores/shooter_formset.html", context)
Input Form Sample
My cleaned_data prints: [{'shooter_number': 67, 'squad': 'Woodland', 'name': 'Legolas', 'DELETE': False, 'id': None, 'shooter_score': 39, 'squad_id': 'Woodland'}]
So it seems that the squad_id has changed right? Wrong. When I look in my db my squad_id is <null>
How do I fix that?
Python: 3.5.4 Django: 1.8
I fixed it by not committing the save before all the changes were made:
if squad_form.is_valid():
squad = squad_form.save(commit=False)
if formset.is_valid():
shooters_to_save = list()
for f in formset.forms:
shooter = f.save(commit=False)
shooter.squad = squad
shooters_to_save.append(shooter)
squad.save()
for shooter in shooters_to_save:
shooter.save()
return redirect('anasp:mainpage')

How to map foreign key when serializing POST data?

I'm trying to map a foreign key to POST data when creating a new object through a serializer. There are two foreign keys in the object, one is serializing perfectly, the other is creating an error.
Model:
class Event(models.Model):
owner = models.ForeignKey('auth.User', related_name='owner', blank=True)
date = models.DateField('eventdate')
time = models.TimeField('eventtime', default=now)
eventtype = models.ForeignKey(EventType, related_name='eventtype', blank=True)
# duration = models.DurationField()
location = models.CharField(max_length=200, blank=True)
attenders = models.ManyToManyField(User, related_name='attenders')
invited = models.ManyToManyField(User, related_name='invitedlist')
View:
class EventMixin(RetrieveUpdateDestroyAPIView, CreateAPIView):
serializer_class = EventSerializer
def get_queryset(self):
return Event.objects.all()
def partial_update(self, request, *args, **kwargs):
request['owner'] = request.user
sname = request['eventtype']
request['eventtype'] = EventType.objects.filter(sname=sname)
json_str = json.dumps(self.request.data)
data = json.loads(json_str)
try:
invited = list(data['toInvite'])
for i in invited:
for j in User.objects.filter(username=i):
invite = EventInvite(invited=j, sender=request.user, event=self.get_object())
invite.save()
self.get_object().invited.add()
except KeyError:
pass
return super(EventMixin, self).partial_update(request, *args, **kwargs)
def create(self, request, *args, **kwargs):
new = {}
new['owner'] = request.user.__dict__
new['date'] = request.data['date']
new['time'] = request.data['time']
new['location'] = request.data['location']
sname = request.data['eventtype']
new['eventtype'] = EventType.objects.get(sname=sname).__dict__
json_str = json.dumps(self.request.data)
data = json.loads(json_str)
serializer = EventMixinSerializer(data=new)
with open('/tmp/log.txt', 'w+') as f:
f.write(str(serializer.is_valid()))
f.write(str(serializer.validated_data))
f.close()
serializer.is_valid();
serializer.save()
try:
invited = list(data['toInvite'])
for i in invited:
for j in User.objects.filter(username=i):
invite = EventInvite(invited=j, sender=request.user, event=self.get_object())
invite.save()
self.get_object().invited.add()
except KeyError:
pass
Serializer:
class EventMixinSerializer(serializers.ModelSerializer):
owner = UserSerializer(read_only=True)
eventtype = EventTypeSerializer()
attenders = FriendsListingField(read_only=True)
invited = FriendsListingField(read_only=True)
class Meta:
model = Event
fields = ('owner', 'eventtype', 'date', 'time', 'location', 'id', 'attenders', 'invited')
def create(self, validated_data):
owner = validated_data.pop('owner')
owner = owner.instance
eventtype = validated_data.pop('eventtype')
eventtype = eventtype.instance
event = Event.objects.create(owner=owner, eventtype=eventtype, **validated_data)
event.save()
return event
Error when owner field present:
False
{'owner': OrderedDict([('username', ['A user with that username already exists.'])])}
Result when UserSerializer(read_only=True) (pretty much diabling it):
True
OrderedDict([('eventtype', OrderedDict([('lname', 'Swimming'), ('sname', 'SWM'), ('category', '1')])), ('date', datetime.date(2015, 12, 22)), ('$
(Notice the event type data in the result)
Thanks!
You need to remove the validators from UserSerializer.
Assuming UserSerializer is a User ModelSerializer it'll extract the unique constraint on the User.username from the Model and your validation will fail.
To work this around you'll need to remove the UniqueValidator by overriding the validators list for the username field of the UserSerializer

Django models and views

I am trying to create my first project. Like Stack Overflow questions and answers on specific theme. I create my own user by AbstractBaseUser. And now I want to create Question and Answer model.
My model:
class Question(models.Model):
text = models.TextField()
title = models.CharField(max_length=200)
date = models.DateTimeField(default=datetime.datetime.now)
author = models.ForeignKey(CustomUser)
def __str__(self):
return self.title
class Answer(models.Model):
text = models.TextField()
date = models.DateTimeField(default=datetime.datetime.now)
likes = models.IntegerField(default=0)
author = models.ForeignKey(CustomUser)
question = models.ForeignKey(Question)
class Tags(models.Model):
TYPE_OF_TAGS = (
('First tag', 'First'),
('Second tag', 'Second'),
('Third tag', 'Third'),
)
type_of_tag = models.CharField(max_length=12, choices=TYPE_OF_TAGS, blank=True)
question = models.ManyToManyField(Question)
my views:
def question(request):
args = {}
args.update(csrf(request))
args['form'] = QuestionForm()
args['profile'] = CustomUser.objects.filter(pk = request.user.pk)
args['author'] = Question()
if request.POST:
args['form'] = QuestionForm(request.POST)
obj = args['form'].save(commit=False)
obj.save()
args['author'].author = args['profile']
args['author'].save()
return redirect(reverse(question))
return render(request, 'questions.html', args)
I create first view for answer, and I have error when I send form:
null value in column "author_id" violates not-null constraint
I think I'm going in the wrong direction. And I have bad models and bad views. Why I have this error and what wrong with my models?
If your business rules don't allow to have null author on a question, you must set the author value to your obj before saving it.
Something like:
def question(request):
args = {}
args.update(csrf(request))
args['form'] = QuestionForm()
args['profile'] = CustomUser.objects.filter(pk = request.user.pk)
if request.POST:
args['form'] = QuestionForm(request.POST)
obj = args['form'].save(commit=False)
obj.author = args['profile']
obj.save()
return redirect('your_redirect_url')
return render(request, 'questions.html', args)
You have another option:
You can pass to the QuestionForm an initial dictionary of:
{
'author' : CustomUser.objects.filter(pk = request.user.pk)
}
Something like:
def question(request):
args = {}
args.update(csrf(request))
args['form'] = QuestionForm(initial={
'author': CustomUser.objects.filter(pk = request.user.pk)
})
if request.POST:
args['form'] = QuestionForm(request.POST)
obj = args['form'].save()
return redirect('your_redirect_url')
return render(request, 'questions.html', args)
NOTE: It's important to render in your questions.html template, a hidden field for the author value, so it is included in the POST data.
Use null = True for Foreign key fields, that can be null.
class Question(models.Model):
#...
author = models.ForeignKey(CustomUser, null = True, blank = True)
Otherwise, specify the value of author when saving the model in first place, which you are doing erroneously. I'm unable to understand what does args['author'] = Question() means? You should do something on the lines of
args['author'] = CustomUser() # in your question view
instead.

Categories