ModelForm not getting saved on server - django

I have a view that uses a modelform to create circles. This works perfectly fine on localhost but on server, the data is not getting created since "save" method is not working.
ModelForm:
class CircleForm(forms.ModelForm):
class Meta:
model = Circles
fields = ('heading','description',)
#exclude = ['current_status', 'progress', 'Owner', 'team_tag', 'exceptional_tag','Team']
def __init__(self, *args, **kwargs):
super(CircleForm, self).__init__(*args, **kwargs)
for name, field in self.fields.items():
if field.widget.attrs.has_key('class'):
field.widget.attrs['class'] += ' text'
else:
field.widget.attrs.update({'class':'text'})
Model:
class Circles(CommonInfo):
CURRENT_STATUS = (('P', 'pending'),
('C', 'complete'),
('ED', 'exceeded deadline'),
('I', 'incomplete'))
Owner = models.ForeignKey(OrganisationUser, related_name="owner_name", blank=True, null=True)
Team = models.TextField(default='', blank=True, null=True)
heading = models.CharField(default='', max_length=100)
description = models.CharField(default='', max_length=250)
progress = models.PositiveIntegerField(default=0, null=True, blank=True)
deadline = models.DateField(null=True,blank=True)
current_status = models.CharField(choices=CURRENT_STATUS, max_length=2, default='P')
team_tag = models.ForeignKey("certificates.Tag", related_name="team_tag_given", blank=True, null=True)
exceptional_tag = models.ForeignKey("certificates.Tag", related_name="exeptioml_tag_given", blank=True, null= True)
View:
def non_ajax_post(self, request, *args, **kwargs):
circle_form = CircleForm(request.POST)
if circle_form.is_valid():
try:
circle = circle_form.save(commit=False)
circle.Owner = request.user.organisation_user
#circle.Team = json.dumps(request.POST.get('hidemps_id'))
team_tag = Tag.objects.get(pk = request.POST.get('team_tag_id'))
exceptional_tag = Tag.objects.get(pk = request.POST.get('exceptional_tag_id'))
circle.team_tag = team_tag
circle.exceptional_tag = exceptional_tag
deadline = string_time_to_datetime(request.POST.get('deadline'), type_of_date='m/d/y')
if deadline < datetime.now():
request.session['deadline_error'] = True
return HttpResponseRedirect(request.path)
circle.deadline = deadline
circle.save()
request.session['circle_saved'] = True
return HttpResponseRedirect(request.path) #here a different context is sent on page load
except Exception as e:
print e.message
else:
return HttpResponseRedirect(request.path)
return HttpResponseRedirect(request.path)
This code works the way I want it and creates a circle. But this happens when I perform this on localhost. When I try it on the server, it doesn't work.
The problem lies in the line "circle.save()"
I used multiple print statements to find out the problem. The printing stops when it encounters "circle.save()" line. After that it redirects me to the same page.
Can anyone suggest some solutions?

Related

Foreign Key Constraint Failure - Django. Manual assignment of Foreign Key to Model via POST Request

I'm working on a timesheet based system currently. I am getting a Foreign Key constraint failure when I am trying to assign the foreign key of one model to the other one.
Here are the two models
class Timesheet(models.Model):
id = models.CharField(primary_key=True, max_length=50, blank=True, unique=True, default=uuid.uuid4)
First_Name = models.CharField(max_length=32)
Last_Name = models.CharField(max_length=32)
Date_Created = models.DateField(auto_now_add=True)
Creator = models.ForeignKey(User, on_delete=models.DO_NOTHING)
Approved = models.BooleanField(default=False)
Company_Relationship = (
('Supplier', 'Supplier'),
('Contractor', 'Contractor'),
('Employee', 'Employee')
)
Worker_Type = models.CharField(max_length=32, choices=Company_Relationship)
Total_Days_Worked = models.DecimalField(decimal_places=2, max_digits=3)
class Meta:
ordering = ['-id']
#unique_together = ['Creator', 'Work_Week']
def get_absolute_url(self):
return reverse('timesheet-detail', kwargs={'pk': self.pk})
class InternalSheet(models.Model):
id = models.CharField(primary_key=True, max_length=50, blank=True, unique=True, default=uuid.uuid4)
Timesheet_id = models.ForeignKey(Timesheet, on_delete=models.DO_NOTHING)
Working_For = (
('7', 'Seven'),
('i', 'intelligent'),
('S', 'Sata'),
)
iPSL = (
('R16.1', 'Release 16.1'),
('R16', 'Release 16')
)
Company_name = models.CharField(max_length=5, choices=Working_For)
Company_name_change = models.CharField(max_length=5, choices=Working_For)
Internal_Company_Role = models.CharField(max_length=10, choices=iPSL)
DaysWorked = models.DecimalField(decimal_places=2, max_digits=3)
Managers = (
('GW', 'Greg Wood'),
('JC', 'Jamie Carson')
)
ManagerName = models.CharField(max_length=8, choices=Managers)
Approved = models.BooleanField(default=False)
def get_absolute_url(self):
return reverse('sheet-detail', kwargs={'pk': self.pk})
My issue is that I am getting a foreign key failure using this post request.
class TimesheetCreateView(LoginRequiredMixin, CreateView):
"""
Creates view and send the POST request of the submission to the backend.
"""
def get(self, request, *args, **kwargs):
internal_form_loop = create_internal_form_for_context()
context = {'form': CreateTimesheetForm(), 'UserGroup': User()}
context.update(internal_form_loop)
print("new", context)
return render(request, 'create.html', context)
def post(self, request, *args, **kwargs):
form = CreateTimesheetForm(request.POST)
internal_form_1 = CreateInternalSheetForm(request.POST)
if form.is_valid():
print("forms valid")
external_timesheet = form.save(commit=False)
print("self", self.request.user)
print("id", Timesheet.id)
external_timesheet.Creator = self.request.user
external_timesheet.save()
else:
print("Error Here")
if internal_form_1.is_valid():
print("Internal form valid")
internal = internal_form_1.save(commit=False)
internal.Timesheet_id_id = Timesheet.id
internal.id = uuid.uuid4()
internal.save()
return HttpResponseRedirect(reverse_lazy('timesheet-detail', args=[Timesheet.id]))
return render(request, 'create.html', {'form': form, 'UserGroup': User()})
It is failing on the line internal.save(). If I print the line internal.Timesheet_id_id I get a value like this, <django.db.models.query_utils.DeferredAttribute object at 0x000001580FDB75E0>. I'm guessing this is the issue? I need the actual Foreign key and not the location of that object. How do I do this. Thanks.
Figured out the issue, I had to replace the lines
internal.Timesheet_id_id = Timesheet.id
internal.id = uuid.uuid4()
internal.save()
with
internal.Timesheet_id_id = Timesheet.objects.get(id=external_timesheet.id)
internal.save()

How to extract "label" and "help_text" data from Django Rest Framework serializer?

I have defined Item model in models.py exactly like below.
class Item(models.Model):
transaction_id = models.CharField(max_length=25, unique=True)
order = models.ForeignKey(Order, on_delete=models.CASCADE)
item_type = models.ForeignKey(ItemType, on_delete=models.SET_DEFAULT, default=1)
title = models.CharField(max_length=200)
description = models.TextField(null=True, max_length=3000)
seller_user_id = models.IntegerField(null=True)
buyer_user_id = models.IntegerField(null=True)
listing_id = models.IntegerField(null=True)
creation_tsz = models.DateTimeField()
price = models.DecimalField(max_digits=9, decimal_places=2)
shipping_cost = models.DecimalField(max_digits=9, decimal_places=2)
quantity = models.IntegerField()
currency_code = models.CharField(null=True, max_length=5)
product_data = JSONField(null=True)
personalization = models.TextField(max_length=1000, null=True)
I have also defined my ItemSerializer with part below.
class ItemSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(read_only=True)
transaction_id = serializers.CharField(read_only=True, max_length=25)
title = serializers.CharField(max_length=200)
description = serializers.CharField(allow_null=True, max_length=3000)
seller_user_id = serializers.IntegerField(allow_null=True, validators=[validate_positive])
buyer_user_id = serializers.IntegerField(allow_null=True, validators=[validate_positive])
listing_id = serializers.IntegerField(allow_null=True, validators=[validate_positive])
creation_tsz = serializers.DateTimeField()
price = serializers.DecimalField(label='Price', max_digits=9, decimal_places=2, validators=[validate_positive])
shipping_cost = serializers.DecimalField(max_digits=9, decimal_places=2, validators=[validate_positive])
quantity = serializers.IntegerField(read_only=True)
currency_code = serializers.CharField(allow_null=True, max_length=5)
product_data = serializers.CharField(allow_null=True)
personalization = serializers.CharField(max_length=1000, allow_null=True)
def update(self, item:Item, validated_data):
#Validating whether
if 'product_data' in validated_data:
schema_obj = item.item_type.schema
try:
print(validated_data)
jsonschema.validate(
json.loads(validated_data['product_data']),
schema=schema_obj)
except Exception as e:
raise ValidationError({'product_data':[f'Schema for product data is not valid. {str(e)}']})
for key in validated_data:
setattr(item, key, validated_data[key])
item.save()
return item
def validate_product_data(self, value):
"""
Validate whether property is json parsable
:param value:
:return:
"""
try:
cur_obj = json.loads(value)
return value
except Exception as e:
raise ValidationError("This field should be in format of JSON.")
In this problem I want frontend application(a VueJS app) to automatically show up error message dialog based on serializers label and ValidationError messages. Thus I have decided to use custom exception handler by changing settings.py like below.
....
REST_FRAMEWORK = {
....
'EXCEPTION_HANDLER': 'OrderManagement.utils.exception_handler',
....
}
....
And finally exception handler function was like below.
def exception_handler(exc, context):
response = views.exception_handler(exc, context)
if isinstance(exc, exceptions.ValidationError):
response.data['validation_meta'] = {key: {'nicename': NICE_NAME_DICT.get(key, key)} for key in exc.detail}
ser = context['view'].get_serializer_class()()
if isinstance(exc, (exceptions.NotAuthenticated)) and response:
response.status_code = status.HTTP_401_UNAUTHORIZED
if response:
response.data['errcode']=response.status_code
return response
I was able to get serializer into "ser" variable but I was not able to get all fields with "label" properties.
After gathering label and/or help_text information my goal is to produce rest response like below for my VueJS app.
{
"price":[
"Price should be a number"
],
"metadata":{
"price":{
"label":"Price",
"help_text":""
}
}
}
I have tried to get it by creating object of related serializer and use "get_fields()" method. I have also tried to gather field specific data on class properties.
How can I extract "label" properties of all fields from 'ser' variable(which is context serializer)? I guess "help_text" property would be gathered with similar methodology.
Code below worked so well. Firstly there should be instantiated an object from ItemSerializer and fields dictionary has all fields with label and help_text data.
def exception_handler(exc, context):
response = views.exception_handler(exc, context)
if isinstance(exc, exceptions.ValidationError):
ser = context['view'].get_serializer_class()
ser_obj = ser()
response.data['validation_meta'] = {}
for key in exc.detail:
if key in ser_obj.fields:
response.data['validation_meta'][key] = {'label': ser_obj.fields[key].label, 'help_text': ser_obj.fields[key].help_text}
if isinstance(exc, (exceptions.NotAuthenticated)) and response:
response.status_code = status.HTTP_401_UNAUTHORIZED
if response:
response.data['errcode']=response.status_code
return response

Auto save user in Django inline formsets with Class-based views and crispy forms

I am using Django inline formsets with Class-based views and crispy forms. Currently, I have a requirement in my project where a User can submit a request for a new Study Concept and any user in the Application can provide comments and share their opinion for a Study Concept. When a user enters her or his comments and submit the form, I want to auto save the currently logged in user to the user field.
For example, User A created a new Study Concept and provided a comment (Please approve my request) while creating the new request. Then for the above comment user A should be saved.
Later User B comes and edit this request and comments " Request looks good to me" and updates the form. Now user B should be auto saved for this comment.
This is a new requirement for the project. I have tried various solutions available on stack-overflow, but I haven't succeeded yet. You can check in my code what currently I am trying to achieve.
Please find my code below:
models.py:
class StudyRequestConcept(models.Model):
CHOICES = (
('Yes', 'Yes'),
('No', 'No'),
)
REQUEST_STATUS = (
('Pending', 'Pending'),
('Approved', 'Approved'),
('Denied', 'Denied'),
)
APPROVER_CHOICES = (
('Amey Kelekar', 'Amey Kelekar'),
)
requestor_name = models.CharField(max_length=240, blank=False, null=False)
project = models.CharField(max_length=240, blank=False, null=False)
date_of_request = models.DateField(blank=False, null=False)
brief_summary = models.CharField(max_length=4000, blank=False, null=False)
scientific_question = models.CharField(max_length=2000, blank=False, null=False)
strategic_fit = models.CharField(max_length=2000, blank=False, null=False)
collaborators = models.CharField(max_length=1000, blank=False, null=False)
risk_assessment = models.CharField(max_length=2000, blank=False, null=False)
devices = models.CharField(max_length=1000, blank=False, null=False)
statistics = models.CharField(max_length=2000, blank=False, null=False)
personnel = models.CharField(max_length=1000, blank=False, null=False)
sample_size = models.PositiveIntegerField(blank=False, null=False, default=0)
population = models.CharField(max_length=2000, blank=False, null=False)
dmti_study_start_date = models.DateField(blank=False, null=False)
duration = models.PositiveIntegerField(blank=False, null=False, default=0)
decision_date = models.DateField(blank=False, null=False)
deliverables = models.CharField(max_length=4000, blank=False, null=False)
logistics_flag = models.CharField(max_length=3, choices=CHOICES, default='No')
status = models.CharField(max_length=20, choices=REQUEST_STATUS, default='Pending')
approver_date = models.DateField(blank=True, null=True)
approver_name = models.CharField(max_length=240, choices=APPROVER_CHOICES, blank=True, null=True)
user = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return str(self.id)
def get_absolute_url(self):
return reverse('update_StudyRequestConcept', kwargs={'pk': self.pk})
def get_commentsStudyRequestConcept(self):
return ','.join([str(i) for i in self.commentsStudyRequestConcept.all().values_list('id', flat=True)])
class CommentsStudyRequestConcept(models.Model):
"""
A Class for Study Request Concept Comments.
"""
comments = models.CharField('Comment', max_length=2000, blank=True, null=True)
commented_on = models.DateTimeField(default=timezone.now)
user = models.ForeignKey(User, on_delete=models.CASCADE)
studyRequestConcept = models.ForeignKey(StudyRequestConcept, related_name='commentsStudyRequestConcept', on_delete=models.CASCADE)
def __str__(self):
return str(self.id)
forms.py:
class StudyRequestConceptForm(forms.ModelForm):
requestor_name = forms.CharField(label=mark_safe('<b>Name of Requestor</b>'))
project = forms.CharField(label=mark_safe('<b>On behalf of which project or platform</b> <br/> <i>(What is the portfolio project / asset team / RU which will benefit from this study? At what development stage is it?)</i>'))
date_of_request = forms.DateField(label=mark_safe('<b>Date of Request</b>'),
input_formats=['%Y-%m-%d'],
widget=XDSoftDateTimePickerInput()
)
brief_summary = forms.CharField(label=mark_safe('<b>Brief Summary of Project</b>'))
scientific_question = forms.CharField(label=mark_safe('<b>Scientific Question</b> <br/><i>(What is the question you would like answered? What is the challenge we are trying to address?)</i>'))
strategic_fit = forms.CharField(label=mark_safe('<b>Strategic Fit / Impact Statement </b><br/><i>(What is the rationale for this study? What is the potential value to Pfizer? What asset team would it support, and have you secured endorsement from that asset team?)</i>'))
collaborators = forms.CharField(label=mark_safe('<b>Potential Collaborators (Internal & External)</b> <br/><i>(Who are the key collaborators required to execute the study?)</i>'))
risk_assessment = forms.CharField(label=mark_safe('<b>Risk Assessment</b> <br/> <i>(What are the risks you foresee? If so, what is your proposed mitigation plan?)</i>'))
devices = forms.CharField(label=mark_safe('<b>Devices / imaging modality</b> <br/> <i>(What device(s) will be deployed, if known)</i>'))
statistics = forms.CharField(label=mark_safe('<b>Statistics</b> <br/><i>(Have you consulted with a statistician? If so, with whom?)</i>'))
personnel = forms.CharField(label=mark_safe('<b>Anticipated personnel needs</b> <br/> <i>(Technician(s), Data Manager(s), CTS, etc.)</i>'))
sample_size = forms.IntegerField(label=mark_safe('<b>Anticipated sample size</b>'), min_value=0, max_value=2147483647)
population = forms.CharField(label=mark_safe('<b>Anticipated study population, or animal model</b><br/><i> (Pfizer internal, healthy, specific disease population…)</i>'))
dmti_study_start_date = forms.DateField(label=mark_safe('<b>Anticipated DMTI study start date</b>'),
input_formats=['%Y-%m-%d'],
widget=XDSoftDateTimePickerInput()
)
duration = forms.IntegerField(label=mark_safe('<b>Estimated study duration (months)</b>'), min_value=0, max_value=2147483647)
decision_date = forms.DateField(label=mark_safe('<b>Anticipated Asset Team study start date (or asset decision date)</b><br/><i> (For decision making to inform internal phased study design</i>'),
input_formats=['%Y-%m-%d'],
widget=XDSoftDateTimePickerInput()
)
deliverables = forms.CharField(label=mark_safe('<b>Expected deliverables</b><br/><i> (Device selection & validation, algorithm developed to qualify…)</i>'))
source = forms.CharField(max_length=50, widget=forms.HiddenInput(), required=False)
def clean(self):
requestor_name = self.cleaned_data.get('requestor_name')
project = self.cleaned_data.get('project')
date_of_request = self.cleaned_data.get('date_of_request')
brief_summary = self.cleaned_data.get('brief_summary')
scientific_question = self.cleaned_data.get('scientific_question')
strategic_fit = self.cleaned_data.get('strategic_fit')
collaborators = self.cleaned_data.get('collaborators')
risk_assessment = self.cleaned_data.get('risk_assessment')
devices = self.cleaned_data.get('devices')
statistics = self.cleaned_data.get('statistics')
personnel = self.cleaned_data.get('personnel')
sample_size = self.cleaned_data.get('sample_size')
population = self.cleaned_data.get('population')
dmti_study_start_date = self.cleaned_data.get('dmti_study_start_date')
duration = self.cleaned_data.get('duration')
decision_date = self.cleaned_data.get('decision_date')
deliverables = self.cleaned_data.get('deliverables')
if (dmti_study_start_date not in EMPTY_VALUES) and (dmti_study_start_date < datetime.date.today()):
self._errors['dmti_study_start_date'] = self.error_class([
'The date cannot be in the past.'])
if (dmti_study_start_date not in EMPTY_VALUES) and (decision_date not in EMPTY_VALUES) and (decision_date < dmti_study_start_date):
self._errors['decision_date'] = self.error_class([
'The date cannot be earlier Anticipated DMTI study start date.'])
return self.cleaned_data
class Meta:
model = StudyRequestConcept
exclude = ('user', 'logistics_flag', 'status','approver_date','approver_name')
class CommentsStudyRequestConceptForm(forms.ModelForm):
comments = forms.CharField(label='Comments',
widget=forms.TextInput(attrs={
'class': 'form-control',
}))
def clean(self):
comments = self.cleaned_data.get('comments')
class Meta:
model = CommentsStudyRequestConcept
exclude = ('commented_on', 'user')
CommentsStudyRequestConceptFormset = inlineformset_factory(StudyRequestConcept, CommentsStudyRequestConcept, form=CommentsStudyRequestConceptForm, extra=1)
views.py:
class StudyRequestConceptCreate(CreateView):
model = StudyRequestConcept
form_class = StudyRequestConceptForm
class StudyRequestConceptFormsetCreate(CreateView):
model = StudyRequestConcept
template_name = 'createStudyRequestConcept.html'
form_class = StudyRequestConceptForm
success_url = reverse_lazy('create_StudyRequestConcept')
def get_context_data(self, **kwargs):
data = super(StudyRequestConceptFormsetCreate, self).get_context_data(**kwargs)
if self.request.POST:
data['comment'] = CommentsStudyRequestConceptFormset(self.request.POST, prefix='comments')
else:
data['comment'] = CommentsStudyRequestConceptFormset(prefix='comments')
return data
def form_valid(self, form):
context = self.get_context_data()
comment = context['comment']
with transaction.atomic():
if comment.is_valid():
self.object = form.save(commit=False)
self.object.user = self.request.user
self.object = form.save()
comment.instance = self.object
commentinstance = comment.save(commit=False)
commentinstance.user = self.request.user
commentinstance.save()
messages.success(self.request, StudyRequestConcept.__name__ +' Form ID: '+ str(self.object.id) + ' was submitted successfully')
return super(StudyRequestConceptFormsetCreate, self).form_valid(form)
else:
return self.render_to_response(self.get_context_data(form=form))
class StudyRequestConceptUpdate(UpdateView):
model = StudyRequestConcept
form_class = StudyRequestConceptEditForm
template_name = 'updateStudyRequestConcept.html'
success_url = '/'
class StudyRequestConceptFormsetUpdate(UpdateView):
model = StudyRequestConcept
form_class = StudyRequestConceptEditForm
template_name = 'updateStudyRequestConcept.html'
success_url = reverse_lazy('edit_StudyRequestConcept')
def get_context_data(self, **kwargs):
data = super(StudyRequestConceptFormsetUpdate, self).get_context_data(**kwargs)
if self.request.POST:
data['comment'] = CommentsStudyRequestConceptFormset(self.request.POST, prefix='comments', instance=self.object)
else:
data['comment'] = CommentsStudyRequestConceptFormset(prefix='comments', instance=self.object)
return data
def form_valid(self, form):
context = self.get_context_data()
comment = context['comment']
with transaction.atomic():
self.object = form.save()
if comment.is_valid():
comment.instance = self.object
commentinstance = comment.save(commit=False)
commentinstance.user = self.request.user
commentinstance.save()
messages.success(self.request, (StudyRequestConcept.__name__) +' Form ID: '+ str(self.object.id) + ' was updated successfully')
return super(StudyRequestConceptFormsetUpdate, self).form_valid(form)
Since 'commented_on' field defaults to timezone.now, it automatically saves it to the table. But it's not the same case with the user.
The error I am getting is:
IntegrityError at /1/updateStudyRequestConcept/ RequestPortal_commentsstudyrequestconcept.user_id may not be NULL
Any help or advice will be appreciated! I will be more than happy to provide any further code or information needed. Thanks in advance for all the support and help.
Regards,
Amey Kelekar
I believe instead of saying this as an answer to my question, I will say that it is a workaround. Since I just require the name of the person who wrote the comment, I updated my models.py as below:
class CommentsStudyRequestConcept(models.Model):
"""
A Class for Study Request Concept Comments.
"""
comments = models.CharField('Comment', max_length=2000, blank=True, null=True)
commented_on = models.DateTimeField(default=timezone.now)
commented_by = models.CharField(max_length=2000, blank=True, null=True)
studyRequestConcept = models.ForeignKey(StudyRequestConcept, related_name='commentsStudyRequestConcept', on_delete=models.CASCADE)
def __str__(self):
return str(self.id)
I removed the Foreign key user and replaced it with a non-mandatory character field commented_by. Accordingly, I updated my forms.py as below:
class CommentsStudyRequestConceptForm(forms.ModelForm):
comments = forms.CharField(label='Comments',
widget=forms.TextInput(attrs={
'class': 'form-control',
}),
required=False)
commented_by = forms.CharField(label='Commented By',
widget=forms.TextInput(attrs={
'class': 'form-control',
'readonly': 'readonly'
}),
required=False)
def clean(self):
comments = self.cleaned_data.get('comments')
commented_by = self.cleaned_data.get('commented_by')
class Meta:
model = CommentsStudyRequestConcept
exclude = ('commented_on', )
I also made sure that the field commented_by is read-only.
In my views.py, once the form is valid, I updated the comments table as below:
#While creating a new Study concept
CommentsStudyRequestConcept.objects.filter(studyRequestConcept_id=self.object.id).update(commented_by=self.request.user.first_name + " " + self.request.user.last_name)
#while adding a new comment to the Study concept:
CommentsStudyRequestConcept.objects.filter(studyRequestConcept_id=self.object.id).filter(commented_by__exact='').update(commented_by=self.request.user.first_name + " " + self.request.user.last_name)
Any better solution is welcomed. Please note that in this solution you have to provide First and Last name while creating the User inorder to make this logic work without any concerns. If you want you can keep the Foreign key user instead of commented_by but make it non-mandatory and use the same logic as discussed above.
Regards,
Amey Kelekar

How can I create a related object in Django 2.0+, simply and effectively?

I'd like to find a simple and robust way to create a child object. I think it is a simple problem, probably solved using Django RelationshipManager or Related objects reference.
I've gotten it to work in the past (by paying someone on fiver to help me solve this), but I feel that there is a much simpler method that escapes me.
This worked on my views.py
class MainVisitForm(SingleObjectMixin, FormView):
template_name = "clincher/visit_form.html"
form_class = VisitForm
model = Main
def post(self, request, *args, **kwargs):
if not request.user.is_authenticated:
return HttpResponseForbidden()
self.object = self.get_object()
form=self.get_form()
form.fk_visit_user = self.request.user
form.fk_visit_main = Main.objects.get(id=self.kwargs['pk'])
#added this to save form as we are mixxing the two forms and Models
# as the approch of singleObjectMixin is we should get object from DB as per request url as a primary key
#and we have defined model as a Main but taking the form view of VistForm as the probem occures
# as i think
if form.is_valid():
instance = Main()
instance.firstname = form.cleaned_data['firstname']
instance.middelname = form.cleaned_data['middlename']
instance.lastname = form.cleaned_data['lastname']
instance.date_of_birth = form.cleaned_data['date_of_birth']
instance.sex = form.cleaned_data['sex']
instance.address = form.cleaned_data['address']
instance.save()
return super().post(request, *args, **kwargs)
def get_success_url(self):
return reverse('clincher:main_detail', kwargs={'pk': self.object.pk})
Basically, while the user is in the details page of the "main" object, I would like them to be able to create a child object (visit object). Ultimately 1 patient will have many visits (1:m relationship). Each time a patient visits the doc, 1 new visit will be added, that is related to that person. Later, I will show a list of visits for that patient (but not the subject of this question).
Models.py
class Main(models.Model):
firstname = models.CharField(max_length = 256, verbose_name=('First Name'))
middlename = models.CharField(max_length=256, verbose_name=('Middle Name'))
lastname = models.CharField(max_length=256, verbose_name=('Last Name'))
date_of_birth = models.DateField()
age = models.CharField(max_length=4)
sex_list = (
(str(1), 'Female'),
(str(2), 'Male'),
(str(3), 'Other'),
(str(4), 'Unknown'),)
sex = models.CharField(max_length = 24, choices=sex_list, verbose_name='Sex')
address = models.TextField(max_length = 256)
#property
def full_name(self):
#"Returns the person's full name."
return '%s %s' % (self.firstname, self.lastname)
#Redirects after form is submitted using primary key
def get_absolute_url(self):
return reverse('clincher:main_detail', kwargs={'pk': self.pk})
def __str__(self):
return self.firstname + ' ' + self.lastname +' - ' + str(self.date_of_birth)
class Visit(models.Model):
fk_visit_main = models.ForeignKey(Main, on_delete=models.CASCADE, verbose_name=('Patient Name'))
visit_date = models.DateField(auto_now = True, editable=True)
fk_visit_user = models.ForeignKey(User, on_delete=models.PROTECT, verbose_name=('Practitioner'), max_length=500)
visit_label = models.CharField(max_length=256, blank=True, null=True)
visit_type = models.CharField(
max_length=256,
default=1, verbose_name='Type of Visit')
visit_progress_notes = models.TextField(max_length=10000,
blank=True, verbose_name='Progress Notes')
outcomes = models.BooleanField(default=False)
def __str__(self):
return '%s %s' % (self.visit_date, self.visit_label)
def get_absolute_url(self):
return reverse('clincher:main_detail', kwargs={'pk': self.pk})
forms.py
class VisitForm(forms.Form):
visit_types_list = (
(str(1), 'Consultation'),
(str(2), 'Procedure'),
(str(3), 'Administrative'),)
visit_type = forms.ChoiceField(choices=visit_types_list)
visit_label = forms.CharField(label='Visit Label', max_length=100)
progress_note = forms.CharField(widget=forms.Textarea)
def form_valid(self, form):
form.instance.fk_visit_user = self.request.user
form.instance.fk_visit_main = Main.objects.get(id=self.kwargs['pk'])
return super().form_valid(form)
Thus, I should end up with a child record/object that has the primary key of the parent object.
The above code works, but I am sure that there is a simple Django-ey way of doing things much simpler, and in a robust manner. I think my solution should be found in the Django RelationshipManager, but I cannot find a solution that works. I paid a guy on Fiver, and I think he didn'y get this as simple as possible.
Check django InlineFormset: https://docs.djangoproject.com/en/2.2/topics/forms/modelforms/#inline-formsets
If you want to have to abillity to add an remove the formset dynamically checkout (Jquery based) :
https://github.com/elo80ka/django-dynamic-formset
If you are using class based view you will have to add the inlineformset in get_context_data() and inside form_valid() check if the formset.is_valid() and then save it to the database.
EDIT: Here is the code based on your comment
forms.py
class VisitForm(forms.ModelForm);
class Meta:
model = Visit
fields = [
'visit_type',
'visit_label',
'visit_progress_notes'
]
views.py
class CreateVisitView(CreateView):
model = Visit
form_class = VisitForm
template_name = "clincher/visit_form.html"
#one of the first function called in class based view, best place to manage conditional access
def dispatch(self, request, *args, **kwargs):
if not request.user.is_authenticated:
return HttpResponseForbidden()
return super(CreateVisitView,self).dispatch(request, *args, **kwargs)
def form_valid(self, form):
visit = form.save(commit=False)
visit.fk_visit_user = self.request.username
visit.fk_visit_main = get_object_or_404(Main, pk=self.kwargs.get('pk'))
visit.save()
return super(CreateVisitView,self).form_valid(form)
models.py
class Main(models.Model):
SEX_LIST_CHOICE = (
(str(1), 'Female'),
(str(2), 'Male'),
(str(3), 'Other'),
(str(4), 'Unknown'),
)
firstname = models.CharField(max_length = 256, verbose_name=('First Name'))
middlename = models.CharField(max_length=256, verbose_name=('Middle Name'))
lastname = models.CharField(max_length=256, verbose_name=('Last Name'))
date_of_birth = models.DateField()
age = models.PositiveSmallIntegerField()
sex = models.CharField(max_length = 24, choices=SEX_LIST_CHOICE, verbose_name='Sex')
address = models.TextField(max_length = 256)
#property
def full_name(self):
#"Returns the person's full name."
return '%s %s' % (self.firstname, self.lastname)
#Redirects after form is submitted using primary key
def get_absolute_url(self):
return reverse('clincher:main_detail', kwargs={'pk': self.pk})
def __str__(self):
return self.firstname + ' ' + self.lastname +' - ' + str(self.date_of_birth)
class Visit(models.Model):
VISIT_TYPE_CHOICE = (
(str(1), 'Consultation'),
(str(2), 'Procedure'),
(str(3), 'Administrative'),)
fk_visit_main = models.ForeignKey(Main, on_delete=models.CASCADE, verbose_name=('Patient Name'))
visit_date = models.DateField(auto_now = True, editable=True)
fk_visit_user = models.ForeignKey(User, on_delete=models.PROTECT, verbose_name=('Practitioner'), max_length=500)
visit_label = models.CharField(max_length=256, blank=True, null=True)
#you are storing the type of visit as an
visit_type = models.CharField(
max_length=256,
default=1,
verbose_name='Type of Visit',
choices=VISIT_TYPE_CHOICE
)
visit_progress_notes = models.TextField(max_length=10000,
blank=True, verbose_name='Progress Notes')
outcomes = models.BooleanField(default=False)
def __str__(self):
return '%s %s' % (self.visit_date, self.visit_label)
def get_absolute_url(self):
return reverse('clincher:main_detail', kwargs={'pk': self.pk})
So a number of things here that you could clear up.
instance.middelname = form.cleaned_data['middlename'] Will never work as middlename is incorrect on the instance side.
You can use Main.objects.create(firstname=form.validated_data['firstname'], lastname= .... etc) to create your Model instances
You should probably have the relation from a User to your models be via Main, not Visit. This will allow you to add records for a Visit easier, for example, staff member logging visits instead of customer.
You should lookup CreateView to assist you with the boilerplate of creating an instance.
Rename the Main model. What is it actually? Looks like a Profile to me, but calling it Main isn't very descriptive.
Age should be an integer field. Nobody is 'dave' years old.

Django admin does not show all entities

I've inherited an app created with Django. There is a problem with it: in admin interface, the page lists not all entities (videos), but some (16 of 25). I have no idea, what is this.
Then I run python manage.py shell, and there Video.objects.all(), there are all 25 objects (counted them using len and by iterating them with for loop).
I have found no managers or whatever (maybe I just don't know where to look for them).
On the bottom of admin page: 25 videos, while there are only 16 rows.
Then I add to VideoModelAdmin class list_per_page = 10, paginator show three pages, but only first two of them has any Videos, third shows no rows.
Here are some code.
# admin.py
class VideoModelAdmin(admin.ModelAdmin):
list_display = ['title', 'short_desc', 'author', 'redactor_choise', 'views_num', 'rating', 'is_published']
list_filter = ['is_published', 'redactor_choise']
list_per_page = 10
actions = ['make_published', 'update_comments_count']
exclude = ('file_lq', 'file_hq', )#'thumb',)
def make_published(self, request, queryset):
queryset.update(is_published=1)
make_published.short_description = "Опубликовать выделенные"
def save_model(self, request, obj, form, change):
instance = form.save(commit=False)
instance.author = request.user
instance.save()
return instance
def update_comments_count(self, request, queryset):
for video in queryset:
video.update_comments_count()
update_comments_count.short_description = "Пересчитать комментарии!"
# later there
admin.site.register(Video, VideoModelAdmin)
# models.py
class Video(models.Model):
def make_upload_path(instance, filename):
return 'video/thumbs/' + generate_random_name(filename)
category = models.ManyToManyField(Category, limit_choices_to = {'is_published': 1})
title = models.CharField(max_length=128)
short_desc = models.CharField(max_length=255)
long_desc = tinymce_models.HTMLField(blank=True)
file_lq = models.FileField(upload_to='video/lq/', null=True, blank=True)
file_hq = models.FileField(upload_to='video/hq/', null=True, blank=True)
thumb = models.FileField(upload_to=make_upload_path, blank=True, null=True)
#thumb = fields.ThumbnailField(upload_to=make_upload_path, sizes=settings.VIDEO_THUMB_SIZE, blank=True, null=True)
author = models.ForeignKey(User, editable=False)
redactor_choise = models.BooleanField(default=False)
views_num = models.SmallIntegerField(default=0, editable=False)
comments_num = models.SmallIntegerField(default=0, editable=False)
rating = models.SmallIntegerField(default=0, editable=False)
voters = fields.PickledObjectField(blank=True, editable=False)
created = models.DateTimeField(auto_now_add=True)
is_published = models.BooleanField(default=False)
def get_absolute_url(self):
return "/video/%d" % self.id
def views_num_plus(self):
cursor = connection.cursor()
cursor.execute('update soctv_video set views_num=views_num+1 where id=%d', [self.id])
cursor.close()
def update_comments_count(self):
from threadedcomments.models import ThreadedComment as Comment
self.comments_num = Comment.objects.filter(video=self).count()
self.save()
#cursor = connection.cursor()
#cursor.execute('update soctv_video set comments_num = (select count(*) from soctv_comment where video_id = %s) where id = %s', [self.id, self.id])
#cursor.close()
def update_categories_counts(self):
cursor = connection.cursor()
cursor.execute('update soctv_category set num_items = (select count(*) from soctv_video_category where category_id = soctv_category.id)')
cursor.close()
def is_user_voted(self, uid):
try:
if self.voters[uid]:
return self.voters[uid]
except Exception:
return False
def increment_view_count(self, token):
import md5
token = md5.new(token).hexdigest()
if VideoView.objects.filter(uniquetoken=token).count() == 0:
VideoView(uniquetoken = token, video = self).save()
def view_count(self):
return self.views_num + VideoView.objects.filter(video=self).count()
def __unicode__(self):
return unicode(self.title)
The problem can be that some FK in some of your videos points to something that does not exist.
I had the same problem and this was the reason.
Django will silently fail if the value is not there in the foreign key column.
Add both null and blank attribute to the column
null=True, blank=True
Make sure that you are logged in to the correct account aswell.
In my case, My account did not have permissions to modify <Model>