I am learning and have a change management project where my model contains field confidential which is by default False, however during instance creation so called initiator could tick it as True. This basically means that only users which are part of current record so called signees could open and see the instance. I am trying to apply get_queryset override on my DetailView:
# MOC DetailView
class MocDetailView(LoginRequiredMixin, DetailView):
model = Moc
template_name = 'moc/moc_detail.html'
def get_queryset(self, *args, **kwargs):
qs = super().get_queryset(*args, **kwargs)
for obj in qs:
print(obj)
confidential = obj.confidential
initiator = obj.initiator
coordinators = obj.coordinators.all()
reviewers = obj.reviewers.all()
approvers = obj.approvers.all()
preimplements = obj.preimplements.all()
authorizers = obj.authorizers.all()
postimplements = obj.postimplements.all()
closers = obj.closers.all()
if initiator and initiator == self.request.user and confidential == True:
qs = qs.filter(Q(confidential=True) & Q(initiator=self.request.user))
return qs
for signee in coordinators:
coordinator_name = signee.coordinator_name
if coordinator_name and coordinator_name == self.request.user and confidential == True:
qs = qs.filter(Q(confidential=True) & Q(coordinators__coordinator_name=self.request.user))
return qs
for signee in reviewers:
reviewer_name = signee.reviewer_name
if reviewer_name and reviewer_name == self.request.user and confidential == True:
qs = qs.filter(Q(confidential=True) & Q(reviewers__reviewer_name=self.request.user))
return qs
for signee in approvers:
approver_name = signee.approver_name
if approver_name and approver_name == self.request.user and confidential == True:
qs = qs.filter(Q(confidential=True) & Q(approvers__approver_name=self.request.user))
return qs
for signee in preimplements:
actionee_name = signee.actionee_name
if actionee_name and actionee_name == self.request.user and confidential == True:
qs = qs.filter(Q(confidential=True) & Q(preimplements__actionee_name=self.request.user))
return qs
for signee in authorizers:
authorizer_name = signee.authorizer_name
if authorizer_name and authorizer_name == self.request.user and confidential == True:
qs = qs.filter(Q(confidential=True) & Q(authorizers__authorizer_name=self.request.user))
return qs
for signee in postimplements:
actionee_name = signee.actionee_name
if actionee_name and actionee_name == self.request.user and confidential == True:
qs = qs.filter(Q(confidential=True) & Q(postimplements__actionee_name=self.request.user))
return qs
for signee in closers:
closer_name = signee.closer_name
if closer_name and closer_name == self.request.user and confidential == True:
qs = qs.filter(Q(confidential=True) & Q(closers__closer_name=self.request.user))
return qs
This allows to restrict Users only to those who are my signees, but in case my confidential field is False non of them are able to open those model instances.
Is there any where I introduce logic here to allow straight opening of non confidential model instance?
Appreciate your help...
Update
# MOC model
class Moc(models.Model):
moc_status = FSMField(choices=STATES, default='draft', protected=False)
initiator = models.ForeignKey(User, blank=True, null=True, on_delete=models.CASCADE)
area = models.ForeignKey(Area, related_name='area_moc', blank=True, null=True, on_delete=models.CASCADE)
category = models.ForeignKey(Category, related_name='category_moc', blank=True, null=True, on_delete=models.CASCADE)
confidential = models.BooleanField(default=False)
# Verifier model
class Verifier(models.Model):
moc = models.ForeignKey(Moc, related_name='verifiers', on_delete=models.CASCADE, default='1')
verifier_group = models.CharField(max_length=36, blank=True, null=True)
verifier_name = models.ForeignKey(User, blank=True, null=True, on_delete=models.CASCADE,)
verify_due = models.DateField(blank=True, null=True)
# Coordinator model
class Coordinator(models.Model):
moc = models.ForeignKey(Moc, related_name='coordinators', on_delete=models.CASCADE, default='1')
cooridnator_group = models.CharField(max_length=36, blank=True, null=True)
coordinator_name = models.ForeignKey(User, blank=True, null=True, on_delete=models.CASCADE,)
coordinator_due = models.DateField(blank=True, null=True)
..... so on so forth for all above listed signees.....
I have resolved it by the following code modification:
class MocDetailView(LoginRequiredMixin, DetailView):
model = Moc
template_name = 'moc/moc_detail.html'
context_object_name = 'moc'
def get_object(self, queryset=None):
obj = super(MocDetailView, self).get_object(queryset=queryset)
print(obj)
confidential = obj.confidential
initiator = obj.initiator
# verifiers = obj.verifiers.all()
verifier = obj.verifiers.filter(verifier_name=self.request.user)
print(verifier)
# coordinators = obj.coordinators.all()
coordinator = obj.coordinators.filter(coordinator_name=self.request.user)
print(coordinator)
# reviewers = obj.reviewers.all()
reviewer = obj.reviewers.filter(reviewer_name=self.request.user)
print(reviewer)
# approvers = obj.approvers.all()
approver = obj.approvers.filter(approver_name=self.request.user)
print(approver)
# preimplements = obj.preimplements.all()
preimplement = obj.preimplements.filter(actionee_name=self.request.user)
print(preimplement)
# authorizers = obj.authorizers.all()
authorizer = obj.authorizers.filter(authorizer_name=self.request.user)
print(authorizer)
# postimplements = obj.postimplements.all()
postimplement = obj.postimplements.filter(actionee_name=self.request.user)
print(postimplement)
# closers = obj.closers.all()
closer = obj.closers.filter(closer_name=self.request.user)
print(closer)
if self.request.user.is_superuser or initiator == self.request.user or verifier or coordinator or reviewer or approver or preimplement or authorizer or postimplement or closer and confidential == True:
return obj
elif not confidential:
return obj
else:
raise Http404()
Related
Model.py
class Branch(models.Model): # Branch Master
status_type = (
("a",'Active'),
("d",'Deactive'),
)
name = models.CharField(max_length=100, unique=True)
suffix = models.CharField(max_length=8, unique=True)
Remark = models.CharField(max_length=200, null=True, blank=True)
created_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
create_at = models.DateTimeField(auto_now_add=True)
update_at = models.DateTimeField(auto_now=True)
status = models.CharField(max_length=1, choices = status_type, default = 'a')
def __str__(self):
return self.name
class Vendor(models.Model):
status_type = (
("a",'Active'),
("d",'Deactive'),
)
branch = models.ManyToManyField(Branch)
company = models.CharField(max_length=200)
name = models.CharField(max_length=200)
phone = models.CharField(max_length=11, unique = True)
email = models.EmailField(max_length=254, unique = True)
gst = models.CharField(max_length=15, unique = True)
pan_no = models.CharField(max_length=10, unique = True)
add_1 = models.CharField(max_length=50, null=True, blank = True)
add_2 = models.CharField(max_length=50, null=True, blank = True)
add_3 = models.CharField(max_length=50, null=True, blank = True)
Remark = models.CharField(max_length=200, null=True, blank=True)
created_by = models.ForeignKey(User, on_delete=models.CASCADE)
create_at = models.DateTimeField(auto_now_add=True)
update_at = models.DateTimeField(auto_now=True)
status = models.CharField(max_length=1, choices = status_type, default = 'a')
def __str__(self):
return self.company
form.py
i want save like created_by field
class VendorForm(ModelForm):
class Meta:
model = Vendor
fields = 'all'
exclude = ['created_by', 'branch']
widgets = {
'company':forms.TextInput(attrs={'class':'form-control'}),
'name':forms.TextInput(attrs={'class':'form-control'}),
'phone':forms.TextInput(attrs={'class':'form-control'}),
'email':forms.EmailInput(attrs={'class':'form-control'}),
'gst':forms.TextInput(attrs={'class':'form-control'}),
'pan_no':forms.TextInput(attrs={'class':'form-control'}),
'add_1':forms.TextInput(attrs={'class':'form-control'}),
'add_2':forms.TextInput(attrs={'class':'form-control'}),
'add_3':forms.TextInput(attrs={'class':'form-control'}),
'Remark':forms.Textarea(attrs={'class':'form-control','rows':'2'}),
'status':forms.Select(attrs={'class':'form-control'}),
}
Views.py
I have pass branch in session.
I want to save with branch which is many to many field
def Add_Vendor(request): # for vendor add
msg = ""
msg_type = ""
branch_id = request.session['branch_id']
branch_data = Branch.objects.get(id = branch_id)
form = ""
if request.method == "POST":
try:
form = VendorForm(request.POST)
if form.is_valid:
vendor_add = form.save(commit=False)
vendor_add.created_by = request.user
vendor_add.instance.branch = branch_data.id
vendor_add.save()
form.save_m2m() # for m to m field save
msg_type = "success"
msg = "Vendor Added."
form = VendorForm(initial={'branch':branch_id})
except:
msg_type = "error"
msg = str(form.errors)
print(msg)
else:
form = VendorForm(initial={'branch':branch_id})
context = {
'form':form,
'branch_data':branch_data,
'msg_type':msg_type,
'msg':msg,
'btn_type':'fa fa-regular fa-plus',
'form_title':'Vendor Form',
'tree_main_title':'Vendor',
'v_url':'vendor_page',
'tree_title':'Add Form',
}
return render(request, 'base/vendor_master/form_vendor.html',context)
I would advise not to work with commit=False in the first place:
def Add_Vendor(request): # for vendor add
branch_id = request.session['branch_id']
branch_data = get_object_or_404(Branch, pk=branch_id)
if request.method == 'POST':
form = VendorForm(request.POST, request.FILES)
if form.is_valid():
form.instance.created_by = request.user
form.instance.branch = branch_data.id
vendor_add = form.save()
vendor_add.branch.add(branch_data)
return redirect('name-of-some-view')
else:
form = VendorForm()
context = {
'form': form,
'branch_data': branch_data,
'btn_type': 'fa fa-regular fa-plus',
'form_title': 'Vendor Form',
'tree_main_title': 'Vendor',
'v_url': 'vendor_page',
'tree_title': 'Add Form',
}
return render(request, 'base/vendor_master/form_vendor.html', context)
You can simplify your form by automatically adding form-control to each widget:
class VendorForm(ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for field in self.fields.values():
attrs = field.widget.attrs
attrs['class'] = attrs.get('class', '') + ' form-control'
class Meta:
model = Vendor
exclude = ['created_by', 'branch']
Note: In case of a successful POST request, you should make a redirect
[Django-doc]
to implement the Post/Redirect/Get pattern [wiki].
This avoids that you make the same POST request when the user refreshes the
browser.
Note: You can set a field editable=False [Django-doc]. Then the field does not show up in the ModelForms and ModelAdmins by default. In this case for example with created_by.
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.
Note: Please do not pass messages manually to the template. Django has the messages framework [Django-doc], which allows to add messages to the request, which will then be delivered the next time a template renders these messages. This makes delivering multiple messages convenient, as well as setting different log levels to the messages.
I'm having an issue in DRF tests when creating instance of a model, the status code in response is 'HTTP_201_CREATED' but the instance it self does not exist in the testing db.
here is my model :
class Item(SafeDeleteModel):
owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, verbose_name=_("Owner"))
name = models.CharField(_("Name"), max_length=150)
category = TreeForeignKey('merssis.ItemCategory', on_delete=models.SET_NULL, null=True, verbose_name=_("Category"))
fixed_price = models.FloatField(_("Fixed price"), default=0)
main_pic = ProcessedImageField(verbose_name=_("Main picture"), upload_to='item_pics', processors=[ItemWatermarker()],format='JPEG')
main_pic_thumbnail = ImageSpecField(source='main_pic',
processors=[ResizeToFill(384, 256)],
format='JPEG',
options={'quality': 100})
geo_location = models.PointField(srid=4326, null=True, blank=True, verbose_name=_("Geolocation"))
_safedelete_policy = SOFT_DELETE_CASCADE
def __str__(self):
return self.name
Serializer :
class ItemCreateSerializer(GeoFeatureModelSerializer):
PRICE_TYPE_CHOICES = (
('fixed', _('Fixed') ),
('open', _('Open') ),
)
owner = serializers.HiddenField(default=serializers.CurrentUserDefault())
price_type = serializers.ChoiceField(choices=PRICE_TYPE_CHOICES)
category = serializers.PrimaryKeyRelatedField(queryset=ItemCategory.objects.all(), many=False)#ItemCategorySerializer(many=False)
main_pic = serializers.ImageField(use_url='item_pics')
def validate(self, data):
user = self.context['request'].user
geo_data = data.get('geo_location')
#Validate fixed price value
if data['price_type'] == 'fixed':
if data.get('fixed_price') == None or int(data.get('fixed_price')) <= 0:
raise serializers.ValidationError({"fixed_price" :INVALIDE_PRICE_ERROR})
#Price type is open should explicitly set fixed price to 0
if data['price_type'] == 'open':
data['fixed_price'] = 0
#Validate geo_location
#geo_location post data form ====> {"type":"Point", "coordinates":[37.0625,-95.677068]}
if geo_data:
if not validate_in_country_location(user, geo_data):
raise serializers.ValidationError({"geo_location":OUTSIDE_COUNTRY_MSG})
return data
def create(self, validated_data):
#Remove price_type value since it is not a field in the model
#We used to determine th price type on the serializer only
validated_data.pop('price_type')
return Item(**validated_data)
class Meta:
model = Item
geo_field = 'geo_location'
fields = ( 'owner',
'name',
'price_type',
'category',
'fixed_price',
'main_pic',
'geo_location',
)
The view :
class ItemCreateAPIView(CreateAPIView):
queryset = Item.objects.all()
serializer_class = ItemCreateSerializer
permission_classes = [permissions.IsAuthenticated]
def perform_create(self, serializer, *args, **kwargs):
self.check_object_permissions(self.request, self.request.user)
serializer.save()
The test case :
class ItemTestCase(APITestCase):
def test_create_new_item(self):
"""
Testing Add new item functionality
"""
self.client_1 = APIClient()
self.user_1 = create_new_user(email='tester1#gmail.com', username='tester_1', password='qsdf654654', gender='male')
self.client_1.login(username='tester_1',password='qsdf654654')
image_file = create_test_image()
category = ItemCategory.objects.create(name='SomeCat')
new_item_data = {
'name': 'New Item',
'price_type' : 'open',
'category': str(category.pk),
'main_pic': image_file,
}
response = self.client_1.post(url, new_item_data, format='multipart')
items = Item.objects.filter(name='New Item')
print(response.status_code)
self.assertEqual( response.status_code, status.HTTP_201_CREATED)
self.assertEqual( items.count(), 1)
and when i run the test i get '201' printed in console AND AssertionError: 0 != 1
i'm fvkin confused
In the serializer create() the object was never saved so change:
return Item(**validated_data)
to:
return Item.objects.create(**validated_data) # create the object
How can you update certain model fields when a form is posted using Django's UpdateView?
When the user checks complete = True on the form, and submits it, I want their name and date to be saved to this record (fields not visible on the form). The code below isn't throwing any errors, but it also isn't updating the fields requested.
Is it potentially because the view is already updating this record with the form, so they conflict?
view:
class maintenanceEdit(LoginRequiredMixin,UpdateView,):
model = Maintenance
form_class = EditMaintenance
template_name = 'maintenance_edit.html'
login_url = 'login'
success_url = reverse_lazy('equipmentdashboard')
def form_valid(self, form,):
instance = form.save(commit=False)
instance.user = self.request.user
user = instance.user.first_name +" "+instance.user.last_name
completed = form.instance.completed
dateCompleted = form.instance.dateCompleted
if (dateCompleted is None):
if completed == True:
updateMaintenance = Maintenance.objects.get(id = instance.id)
updateMaintenance.dateCompleted = timezone.now()
updateMaintenance.completedBy = user
updateMaintenance.save(update_fields=['dateCompleted','completedBy',])
return super(maintenanceEdit, self).form_valid(form)
model:
class Maintenance(models.Model):
device = models.ForeignKey(get_user_model(),on_delete=models.CASCADE,)
customerTag = models.CharField(max_length=50,)
maintenanceItem = models.CharField(max_length=35,blank=True,)
maintenanceDescrip = models.TextField(max_length=300,blank=True,)
maintenanceNotes = models.TextField(max_length=300,blank=True,)
dateCreated = models.DateTimeField(auto_now_add=True,)
dateDue = models.DateTimeField(auto_now=False, auto_now_add=False, null=True, blank=True, editable=True)
dateCompleted = models.DateTimeField(auto_now=False, auto_now_add=False, null=True, blank=True, editable=True)
completed = models.BooleanField(default = False)
createdBy = models.CharField(max_length=35,blank=True,)
completedBy = models.CharField(max_length=35,blank=True,)
form:
class EditMaintenance(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(EditMaintenance, self).__init__(*args, **kwargs)
self.fields['maintenanceItem'].required = True
self.fields['dateDue'].required = True
class Meta:
model = Maintenance
fields = ['maintenanceItem','dateDue','maintenanceDescrip',
'maintenanceNotes','completed',]
labels = {
'maintenanceItem': ('Maintenance Item'),
'dateDue': ('Maintenance Due'),
'maintenanceDescrip': ('Maintenance Description'),
'maintenanceNotes': ('Maintenance Notes'),
'completed': ('Complete Maintenance'),
}
I also tried, in my view:
def form_valid(self, form,):
instance = form.save(commit=False)
instance.user = self.request.user
user = instance.user.first_name +" "+instance.user.last_name
completed = form.instance.completed
dateCompleted = form.instance.dateCompleted
if (dateCompleted is None):
if completed == True:
Maintenance.objects.filter(id = instance.id).update(dateCompleted = timezone.now(),completedBy = user)
return super(maintenanceEdit, self).form_valid(form)
For some reason i can create an new instance of classified model but cannot update an existing one neither via my view or through admin panel in Django. I am using postgresql and from the logs, it seems like queries are not hitting the database at all. I removed and resetted the app, even drop the tables but didn't help.
class classified(models.Model):
slug = models.SlugField(unique=True,blank=True, null=True)
submitted_by = models.ForeignKey(User, blank=True, null=True)
title = models.CharField(max_length=120, blank=True, null=True)
point = models.PointField(srid=settings.SRID, blank=True, null=True)
address = models.CharField(max_length=120, blank=True, null=True)
city = models.CharField(max_length=60, blank=True, null=True)
state = models.CharField(max_length=60, blank=True, null=True)
zipcode = models.CharField(max_length=5, blank=True, null=True)
description = models.TextField(max_length=500,blank=True, null=True)
objects = models.GeoManager()
tags = TaggableManager(blank=True)
submission_date = models.DateTimeField(auto_now_add=True, null=True, blank=True)
price = models.CharField(max_length=20, blank=True, null=True)
def __unicode__(self):
#return "%s %s %s"%(self.title, self.point.x, self.point.y)
return "%s"%(self.title)
#models.permalink
def get_absolute_url(self):
return ('listing_detail', (),
{
'slug' :self.slug,
})
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.title)
if not self.point:
location = "%s+%s+%s+%s"%(self.address, self.city, self.state, self.zipcode)
cord = get_lat_long(location)
x,y = cord.split(",")
x = float(x)
y = float(y)
self.point = Point(x,y)
self.point2 = Point(x,y)
super(classified, self).save(*args, **kwargs)
ClassifiedImage model:
class ClassifiedImage(models.Model):
classified = models.ForeignKey(classified, related_name="images", null=True, blank=True)
image = models.ImageField(upload_to='classifieds/%Y/%m/%d', default='static/img/no-thumb.jpg', null=True, blank=True)
Classified View:
def add_classified(request):
userprofile = User.objects.get(pk=request.user.id)
if request.method == 'POST':
form = classifiedForm(request.POST, request.FILES)
if form.is_valid():
classifiedad = form.save(commit=False)
image_formset = ImageFormSet(request.POST, request.FILES, instance=classifiedad)
classifiedad.submitted_by = request.user
classifiedad.save()
image_formset.save()
slug = classifiedad.slug
redirect_to =reverse('classified-detail', kwargs={'slug':slug})
return HttpResponseRedirect(redirect_to)
else:
form = classifiedForm()
image_formset = ImageFormSet()
return render_to_response('shclassified/add_classified.html',{'form':form, 'image_formset':image_formset},context_instance = RequestContext(request))
Classified Update view:
def ClassifiedUpdate(request, slug):
classifiedins = classified.objects.get(slug=slug)
if request.method == 'POST':
form = classifiedForm(request.POST, request.FILES)
if form.is_valid():
#classifiedad = form.save(commit=False)
image_formset = ImageFormSet(request.POST, request.FILES, prefix="images", instance=classifiedins)
#classifiedad.submitted_by = request.user
#classifiedad.save()
#form().save()
image_formset.save()
#slug = classifiedad.slug
return HttpResponseRedirect(".")
else:
form = classifiedForm(instance=classifiedins)
image_formset = ImageFormSet(instance=classifiedins, prefix="images")
return render_to_response('shclassified/add_classified.html',{'form':form, 'image_formset':image_formset},context_instance = RequestContext(request))
You're overriding the save method incorrectly by the looks of things:
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.title)
if not self.point:
location = "%s+%s+%s+%s"%(self.address, self.city, self.state, self.zipcode)
cord = get_lat_long(location)
x,y = cord.split(",")
x = float(x)
y = float(y)
self.point = Point(x,y)
self.point2 = Point(x,y)
super(classified, self).save(*args, **kwargs) ## wrong indentation!
Your super() call should be aligned with the first level of indentation. As you have it, super().save is only called if there is no self.point.
I want to set the form value..i am not displaying it in form but want to set the value of field in my view?
This my modelform:
class payment_detail(models.Model):
status = (
('Paid','Paid'),
('Pending','Pending'),
)
id = models.AutoField(primary_key=True)
#ref_id = models.CharField(max_length=32, default=_createId)
#user = models.ForeignKey(User, editable = False)
payment_type= models.ForeignKey(Payment_types,to_field = 'payment_types', null=True, blank=True)
job_post_id= models.ForeignKey(jobpost,to_field = 'job_id', null=True, blank=True)
price= models.ForeignKey(package,to_field = 'amount', null=True, blank=True)
created_date = models.DateField(("date"), default=datetime.date.today)
payment_status = models.CharField(max_length=255, choices=status,default='Pending')
transaction_id = models.CharField(max_length=255, null=True, blank=True)
payment_date = models.DateField(null=True, blank=True)
email = models.CharField(max_length=255, null=True)
def __unicode__(self):
#return self.user
return unicode(self.id)
#return self.ref_id
return unicode(self.payment_type)
return unicode(self.job_post_id)
return unicode(self.price)
return unicode(self.created_date)
return unicode(self.payment_status)
return unicode(self.payment_date)
return unicode(self.transaction_id)
return unicode(self.email)
admin.site.register(payment_detail)
my View:
def payment(request):
if "pk" in request.session:
pk = request.session["pk"]
Country = request.session["country"]
price = package.objects.filter(item_type__exact='Job' ,country__country_name__exact=Country, number_of_items__exact='1')
if request.method == 'POST':
entity = payment_detail()
form = jobpostForm_detail(request.POST, instance=entity)
if form.is_valid():
#form.fields["transaction_id"] = 100
form.save()
#message = EmailMessage('portal/pay_email.html', 'Madeeha ', to=[form.cleaned_data['email']])
#message.send()
return HttpResponseRedirect('/portal/pay/mail/')
else:
form = jobpostForm_detail(initial={'transaction_id': "US"})
c = {}
c.update(csrf(request))
return render_to_response('portal/display.html',{
'form':form,'price':price
},context_instance=RequestContext(request))
like i want to set the value of job_location and don't want to display it in form..
forms.py
//this is how you hide the field
class jobpostForm(ModelForm):
def __init__(self, *args, **kwargs):
super(jobpostForm, self).__init__(*args, **kwargs)
self.fields['job_location'].widget = forms.HiddenInput()
class Meta:
model = jobpost
views.py
.........
if request.method == 'POST':
entity = payment_detail(transaction_id="US") #change
form = jobpostForm_detail(request.POST, instance=entity)
if form.is_valid():
#form.fields["transaction_id"] = 100
form.save()
#message = EmailMessage('portal/pay_email.html', 'Madeeha ', to=[form.cleaned_data['email']])
#message.send()
return HttpResponseRedirect('/portal/pay/mail/')
else:
form = jobpostForm_detail()
..................