Set the primary_key based on two other fields in Django - django

I have the following model in which one the fields is based on other fields (similar to this):
from django.db import models
class Model_Model01(models.Model):
code_01 = models.CharField(max_length = 2, null = False, blank = False)
code_02 = models.CharField(max_length = 3, null = False, blank = False)
code_combined = models.CharField(max_length = 6, null = True, blank = False, primary_key = False)
def save(self, *args, **kwargs):
self.code_combined = "{}.{}".format(self.code_01, self.code_02)
super(Model_Model01, self).save(*args, **kwargs)
def __unicode__(self):
return self.code_combined
I would like to set the primary_key of code_combined field as True after overriding the save method so I can use it as a foreignkey on other model. Is there a way to do this?

Why not tell Django that you are using a composite key for this model using unique_together?
Composite primary key in django
https://docs.djangoproject.com/en/2.0/ref/models/options/#unique-together
class Model_Model01(models.Model):
code_01 = models.CharField(max_length = 2, null = False, blank = False)
code_02 = models.CharField(max_length = 3, null = False, blank = False)
class Meta:
unique_together = ('code_01', 'code_02',)
Update
Why not have the code_combined field be Unique, and reference it in your ForeignKey as the to_field?
class Model_Model01(models.Model):
code_01 = models.CharField(max_length = 2, null = False, blank = False)
code_02 = models.CharField(max_length = 3, null = False, blank = False)
code_combined = models.CharField(max_length = 6, unique = True)
def save(self, *args, **kwargs):
self.code_combined = "{}.{}".format(self.code_01, self.code_02)
super(Model_Model01, self).save(*args, **kwargs)
def __unicode__(self):
return self.code_combined
class Model_Model02(models.Model):
model_1 = models.ForeignKey(Model_Model01, to_field='code_combined')
...
Beware of Unicode here; unless you are using Python 3!

Related

Filter Foreign Key Drop Down Field Django

I have 3 tables Student, K8Points, and TeacherClass . The table students tracks all student data information and you can assign them to a classroom, points tracks the points they earned for the day, classroom table is where you create the classroom names. What i want to accomplish is when you are logged in to /k8_points_classroom/1? page, there is a drop down field of students. 1 at the end of the classroom is the classroom id. The field student_name should only show the kids in that particular class, how do i filter that field ? Right now it just pulls up every student in the database. Thanks for the help .
Model
class TeacherClass(models.Model):
class_name = models.CharField(max_length = 50, default= "")
teacher_name = models.ManyToManyField(User)
grade = models.CharField(max_length = 2, default= "")
class Meta:
verbose_name = "Teacher Class Room"
def __str__(self):
return self.class_name
class Student(models.Model):
studentpsid= models.CharField(primary_key = True , default = "", max_length = 50, unique = True)
student_name = models.CharField(max_length = 50)
student_grade = models.CharField(max_length = 2, default = "")
home_room = models.CharField(max_length = 3, default = "")
counseling_goal = models.TextField(max_length = 255)
class_name = models.ManyToManyField(TeacherClass)
image =models.ImageField(default ="default.png", upload_to='student_pics')
class K8Points(models.Model):
date = models.DateField(default=datetime.date.today)
class_name = models.ForeignKey(TeacherClass, on_delete = models.PROTECT, default = "",)
student_name = models.ForeignKey(Student,on_delete = models.CASCADE, default ="" ,)
week_of = models.IntegerField(default=weeknumber)
day = models.CharField(max_length= 10, default = dayofweek)
TIME_FRAME_CHOICES = [
(None, 'PLEASE SELECT TIME FRAME'),
(1, '(1.) 8:45AM - 9:00AM'),
(2, '(2.) 9:00AM - 9:30AM'),
(3, '(3.) 9:30AM - 10:00AM'),
(4, '(4.) REC. I 10:00AM -10:10AM'),
(5, '(5.) 10:10AM-10:40AM'),
(6, '(6.) 10:40AM-11:10AM'),
(7, '(7.) 11:10AM-11:40AM'),
(8, '(8.) LUNCH 11:40AM-12:00PM'),
(9, '(9.) REC. II 12:00PM-12:20PM'),
(10, '(10.) 12:20PM-12:50PM'),
(11,'(11.) 12:50PM-1:20PM'),
(12,'(12.) 1:20PM-1:50PM'),
(13,'(13.) 1:50PM-2:20PM'),
(14,'(14.) REC. III 2:20PM-2:30PM'),
]
time_frame = models.PositiveSmallIntegerField(choices=TIME_FRAME_CHOICES,)
behavior = models.IntegerField(default="", validators=[
MaxValueValidator(5),
MinValueValidator(1)
])
academic = models.IntegerField(default="", validators=[
MaxValueValidator(5),
MinValueValidator(0)
] )
created_at = models.DateTimeField(default = timezone.now)
class Meta:
verbose_name = "K8-Points"
def __str__(self):
return self.student_name
Form
def __init__(self, *args, **kwargs):
self.classid = kwargs.pop('classid', None)
super(K8Points_ClassroomForm,self).__init__(*args,**kwargs)
self.fields['date'].disabled = True
self.fields['day'].disabled = True
self.fields['week_of'].disabled = True
self.fields['class_name'].widget.attrs['readonly'] = True
getstudents = Student.objects.filter(class_name = self.classid).order_by('student_name')
self.fields['student_name'].queryset= getstudents.all().order_by('student_name')
View
#login_required
def K8_Points_Classroom(request, classid):
if request.method == "GET":
date = datetime.date.today()
students = Student.objects.filter(class_name=classid).order_by('student_name')
class_name = TeacherClass.objects.get(id=classid)
form = K8Points_ClassroomForm(classid = class_name.id)
request.session['my_class_id'] = classid
return render(request, 'points/k8_points_classroom.html', {'form': form, 'students': students, 'class_name': class_name, 'date': date , 'classid': classid})
You need to pass the class in as a parameter to the form in the view, then adjust the student_name field's queryset to filter down to that class' students.
class K8PointsClassroomForm(forms.ModelForm):
def __init__(self, *args, class_=None, **kwargs):
super (K8Points_ClassroomForm,self).__init__(*args,**kwargs )
self.fields['date'].disabled = True
self.fields['day'].disabled = True
self.fields['week_of'].disabled = True
# Limit the fields queryset to that of the class's students.
self.fields['student_name'].queryset = class_.student_set.all()
Edit:
Here's the view I was trying to explain:
#login_required
def K8_Points_Classroom(request, classid):
class_name = TeacherClass.objects.get(id=classid)
form = K8Points_ClassroomForm(class_=class_name)

Django Restframework “Got AttributeError when attempting to get a value for field `section_type` on serializer `QuestionSerializer`

This is my model.
class QuestionSection(models.Model):
section = models.CharField(max_length = 100, null = True, blank = True)
max_marks = models.IntegerField()
def __str__(self):
return self.section
class Question(models.Model):
question_section = models.ForeignKey(QuestionSection, on_delete = models.CASCADE, related_name = 'questions')
section_type = models.CharField(max_length = 10,)
questions = models.CharField(max_length = 350, null = True, blank = True)
image = models.CharField(max_length = 10, null = True, blank = True)
no_of_images = models.IntegerField(null = True, blank = True)
marks = models.IntegerField()
shop_view = models.CharField(max_length = 30, null = True, blank = True, choices=(('critical', 'critical'), ('major', 'major')))
what_to_look_for = models.CharField(max_length = 350, null = True, blank = True)
def __str__(self):
return "{}-{}".format(self.section_type, self.marks)
This is my serializer
class QuestionSerializer(serializers.ModelSerializer):
class Meta:
model = Question
fields = '__all__'
class QuestionSectionSerializer(serializers.ModelSerializer):
questions = QuestionSerializer(read_only = True)
class Meta:
model = QuestionSection
fields = '__all__'
I am not able to figuring out how to do it. The API will be like QuestionSection fields and inside that Question fields will be there.

Model's fields being cleared after ModelForm.save() called

I'm having an issue where I instantiate a ModelForm-based form with a pre-existing instance of the model in the GET portion of my view function. This model already has several fields filled in; the ModelForm is used to collect other fields in the model. The pre-existing fields are excluded in the ModelForm's definition.
The problem is, during POST processing, after successful validation of the ModelForm, I'm calling ModelForm.save(commit=False)...and the model returned (which should be the same one I passed in as 'instance' in the GET handling, remember) has somehow lost all the fields that were previously set. The fields actually set by the form are fine; but it's no longer the original instance of my model.
This is not the behavior I expected; and in fact I've used this partial-model-in-modelform previously, and it works other places. What am I missing here??
Hopefully some code'll make all this clear...
So here's the Model:
class Order(models.Model):
STATUS = (
('Created', -1),
('Pending', 0),
('Charged', 1),
('Credited', 2),
)
SHIPPING_STATUS = (
('Cancelled', 0),
('Ready for pickup', 1),
('Shipped', 2),
('OTC', 3),
)
orderID = models.IntegerField(max_length=15, null=True, blank=True)
store = models.ForeignKey(Store)
paymentInstrument = models.ForeignKey(PaymentInstrument, null=True, blank=True)
shippingMethod = models.ForeignKey(ShippingMethod, null=True, blank=True)
last_modified = models.DateTimeField(null=True, blank=True)
date = models.DateTimeField(auto_now_add=True, null=True, blank=True)
total = models.FloatField(default=0.0, blank=True)
shippingCharge = models.FloatField(default=0.0, blank=True)
tax = models.FloatField(default=0.0, blank=True)
status = models.CharField(max_length=50, choices=STATUS, default = 'Created')
shippingStatus = models.CharField(max_length=50, choices=SHIPPING_STATUS, default = '1')
errstr = models.CharField(max_length=100, null=True, blank=True)
# billing info
billingFirstname = models.CharField(max_length = 50, blank = True)
billingLastname = models.CharField(max_length = 50, blank = True)
billingStreet_line1 = models.CharField(max_length = 100, blank = True)
billingStreet_line2 = models.CharField(max_length = 100, blank = True)
billingZipcode = models.CharField(max_length = 5, blank = True)
billingCity = models.CharField(max_length = 100, blank = True)
billingState = models.CharField(max_length = 100, blank = True)
billingCountry = models.CharField(max_length = 100, blank = True)
email = models.EmailField(max_length=100, blank = True)
phone = models.CharField(max_length=20, default='', null=True, blank=True)
shipToBillingAddress = models.BooleanField(default=False)
# shipping info
shippingFirstname = models.CharField(max_length = 50, blank = True)
shippingLastname = models.CharField(max_length = 50, blank = True)
shippingStreet_line1 = models.CharField(max_length = 100, blank = True)
shippingStreet_line2 = models.CharField(max_length = 100, blank = True)
shippingZipcode = models.CharField(max_length = 5, blank = True)
shippingCity = models.CharField(max_length = 100, blank = True)
shippingState = models.CharField(max_length = 100, blank = True)
shippingCountry = models.CharField(max_length = 100, blank = True)
Here's the ModelForm definition:
class OrderForm(ModelForm):
class Meta:
model = Order
exclude = ('orderID',
'store',
'shippingMethod',
'shippingStatus',
'paymentInstrument',
'last_modified',
'date',
'total',
'payportCharge',
'errstr',
'status', )
widgets = {
'billingCountry': Select(choices = COUNTRIES, attrs = {'size': "1"}),
'shippingCountry': Select(choices = COUNTRIES, attrs = {'size': "1"}),
'billingState': Select(choices = STATES, attrs = {'size': "1"}),
'shippingState': Select(choices = STATES, attrs = {'size': "1"}),
}
And here is the view function:
def checkout(request):
theDict = {}
store = request.session['currentStore']
cart = request.session.get('cart', False)
order = request.session['currentOrder'] # some fields already set
if not cart: # ...then we don't belong on this page.
return HttpResponseRedirect('/%s' % store.urlPrefix)
if request.method == 'GET':
form = OrderForm(instance=order, prefix='orderForm')
else: # request.method == 'POST':
logging.info("Processing POST data...")
form = OrderForm(request.POST, prefix='orderForm')
if form.is_valid():
### AT THIS POINT, ORDER FIELDS ARE STILL GOOD (I.E. FILLED IN)
order = form.save(commit=False)
### AFTER THE SAVE, WE'VE LOST PRE-EXISTING FIELDS; ONLY ONES SET ARE
### THOSE FILLED IN BY THE FORM.
chargeDict = store.calculateCharge(order, cart)
request.session['currentOrder'] = order
return HttpResponseRedirect('/%s/payment' % store.urlPrefix)
else:
logging.info("Form is NOT valid; errors:")
logging.info(form._errors)
messages.error(request, form._errors)
theDict['form'] = form
theDict['productMenu'] = buildCategoryList(store)
t = loader.get_template('checkout.html')
c = RequestContext(request, theDict)
return HttpResponse(t.render(c))
Any/all help appreciated...
When you instantiate the form during the POST, the instance of the model you're editing is None, because you're not passing it in, and you're not persisting the form instance from the GET. Django won't do anything on its own to persist data between requests for you.
Try:
...
form = OrderForm(request.POST or None, instance=order, prefix='orderForm')
if request.method == 'POST':
logging.info("Processing POST data...")
if form.is_valid():
...
Now the instance will be populated for GET and POST, but the data from request.POST is optional for the form if it's None. It also saves you from having to instantiate the form in two places depending on request.method

Foreign Key related form Saving in Django

My Models are look like .....
Student(models.Model):
name = models.CharField(max_length = 60, blank = False)
r_no = models.CharField(max_length = 60, blank = False)
created_date = models.DateTimeField(null = False, blank = False, default = datetime.datetime.now())
StudentPotential(models.Model):
aka_name = models.CharField(max_length = 60, blank = True)
-----
-----
StudentCorrespondence(models.Model):
student = models.ForeignKey('Student', related_name = "Student_FK")
student_p = models.ForeignKey('Student', related_name = "Student_FK")
emailed_date = models.DateTimeField(null = True, blank = True)
phoned_date = models.DateTimeField(null = True, blank = True)
My Form in form.py
class StudentPotentialForm (forms.ModelForm):
class Meta:
model = StudentPotential
class StudentCorrespondenceForm(forms.ModelForm):
class Meta:
model = StudentCorrespondence
exclude = ('student', 'student_p')
Finally My view.py
def add_student_company_potential(request, student_id):
from cdradmin.forms import StudentPotentialForm, StudentCorrespondenceForm
if request.method == 'POST':
### HOW TO SAVE THE two from for the student have its it 'student_id' ####
else:
StudentPotentialForm = StudentPotentialForm()
StudentCorrespondenceForm = StudentCorrespondenceForm()
context = {'StudentCorrespondenceForm':StudentCorrespondenceForm, "StudentPotentialForm":StudentPotentialForm}
return render_to_response('cdradmin/studentform.html', context, context_instance = RequestContext(request))
Once the data is post to the view, How can i able to save this data for the student has his/her id is 'student_id'
You can try this
if request.method == 'POST':
spf = StudentPotentialForm(request.POST)
if spf.is_valid():
osp = spf.save()
else :
#raise error
student = Student.objects.get(id=student_id)
scf = StudentCorrespondenceForm(request.POST)
if scf.is_valid():
osc = scf.save(commit=False)
osc.student = student
osc.student_p = osp
osc.save()
else:
# raise error.

Thread safe field value counting

Is there any way to do something like this in django?
INSERT INTO t VALUES(1,(SELECT x.c2 FROM t x ORDER BY c2 DESC LIMIT 1)+1,1);
I have a model with many fields. And one of the field's value must be set with accordance to previous record.
Currently I am doing this by simple selecting previous record. But this is awful and not thread safe.
def save(self, *args, **kw):
if self.sn is None or self.sn == ns.DEFAULT_GIFT_SN:
q = Gift.objects.filter(company = self.company).only('id').order_by('-id')
if q.count():
last = q[0]
next_id = int(last.sn) + 1
else:
next_id = 1
self.sn = next_id
super(Gift, self).save(*args, **kw)
I want to sth. lazy like:
def save(self, *args, **kw):
if self.sn is None or self.sn == ns.DEFAULT_GIFT_SN:
self.sn = _F('SELECT x.c2 FROM t x ORDER BY c2 DESC LIMIT 1')
super(Gift, self).save(*args, **kw)
Any suggestions?
UPDATE(for Cesar):
class Gift(models.Model):
company = models.ForeignKey(Company)
certificate = ChainedForeignKey(Certificate,
chained_field = "company",
chained_model_field = "company",
show_all = False,
auto_choose = True
)
gifter = models.ForeignKey(User, null = True, blank = True)
giftant_first_name = models.CharField(_('first name'), max_length = 30, blank = True)
giftant_last_name = models.CharField(_('last name'), max_length = 30, blank = True)
giftant_email = models.EmailField(_('e-mail address'), blank = True)
giftant_mobile = models.CharField(max_length = 20, blank = True, null = True)
due_date = models.DateTimeField()
exp_date = models.DateTimeField()
sn = models.CharField(max_length = 32, default = ns.DEFAULT_GIFT_SN, editable = False)
pin_salt = models.CharField(max_length = 5, editable = False, default = gen_salt)
pin = models.CharField(max_length = 32, null = True, blank = True)
postcard = models.ForeignKey(Postcard)
state_status = models.IntegerField(choices = ns.STATE_STATUSES, default = ns.READY_STATE_STATUS)
delivery_status = models.IntegerField(choices = ns.DELIVERY_STATUSES, default = ns.WAITING_DELIVERY_STATUS)
payment_status = models.IntegerField(choices = ns.PAYMENT_STATUSES, default = ns.NOT_PAID_PAYMENT_STATUS)
usage_status = models.IntegerField(choices = ns.USAGE_STATUSES, default = ns.NOT_USED_USAGE_STATUS)
nominal = models.FloatField(default = 0)
used_money = models.FloatField(default = 0)
use_date = models.DateTimeField(blank = True, null = True)
pub_date = models.DateTimeField(auto_now_add = True)
cashier = ChainedForeignKey(CompanyUserProfile, blank = True, null = True,
chained_field = "company",
chained_model_field = "company",
show_all = False,
auto_choose = True
)
class Meta:
unique_together = ('company', 'sn',)
I know while is evil but you can try something like that :
sn_is_ok = False
while not sn_is_ok:
last_id = MyModel.objects.latest('id').id
try:
self.sn = last_id + 1
self.save()
Except IntegrityError:
continue
else:
sn_is_ok = True
I don't think you get more that 2 loops.
I guess you can do three things, from simple to outraging mad on the extremes:
Let Gift.sn be a AutoField, if you can get away without needing to increment the values separately for each company.
Use a unique_together = ('company', 'sn') constraint and update the gift instance if the uniqueness constraint fails, use the example #Stan provided, with the worst-case scenario that if you have a lot of concurrent writes only one will pass in each turn.
Write some custom SQL to obtain a lock on the Gift model table if the database supports it.