State Control/Workflow in Django - python

I'm having an object with a few fields such as order line, reference no, state
the state is a selection of draft and confirmed
what I want is when the record's state is confirmed, the other field can't be modified/ readonly
what's the best / common way to do this in django ?
thanks

I tired it with 3 example fields.
I create a model with 3 fields:
class New(models.Model):
title = models.CharField(max_length=100,unique=True)
body = models.TextField()
editable = models.BooleanField(default=True)
def __unicode__(self):
return self.title
My forms.py code is:
class MyNewForm(forms.ModelForm):
class Meta:
model = New
def clean(self):
cleaned_data = super(MyNewForm,self).clean()
title = cleaned_data.get('title')
body = cleaned_data.get('body')
editable = cleaned_data.get('editable')
if self.instance.pk:
try:
row = New.objects.get(id=self.instance.pk)
except New.DoesNotExist:
raise forms.ValidationError('Record not found')
if not row.editable and not editable:
raise forms.ValidationError('This record is not editable')
return cleaned_data
And my admin.py code is:
from news.models import New
from news.forms import MyNewForm
class MyNew(admin.ModelAdmin):
form = MyNewForm
admin.site.register(New,MyNew)
Hope it works fine for you.

A true/false field (i.e. the BooleanField) can do the job. Read the docs here: https://docs.djangoproject.com/en/dev/ref/models/fields/#booleanfield

Related

How to use a different unique identifier of Django ForeignKey field in ModelForm

I have the following Models:
class Book(models.Model):
name = models.CharField(max_length=128)
isbn = models.CharField(max_length=13, unique=True)
available = models.BooleanField(default=True)
class Borrow(models.Model):
date = models.DateTimeField(auto_now_add=True)
book = models.ForeignKey(Book)
and the following ModelForm:
class CreateBorrowForm(forms.ModelForm):
class Meta:
model = Borrow
fields = ['book']
def clean_book(self):
book = self.cleaned_data['book']
try:
return Book.objects.get(id=book, available=True)
except Book.DoesNotExist:
raise ValidationError('Book does not exist or it is unavailable')
I would like to have a form that expects the isbn field of the Book model, instead of id. As the isbn field is unique, it does make sense. In the clean_book method I would need to do a little change to have the following line:
return Book.objects.get(isbn=book, available=True)
The problem is that I cannot find an approach to force the form to use a different unique identifier. In my specific case, this is required to avoid brute force enumerating over numerical IDs.
You'd need to use a custom field for that, and override the save() method instead of the clean__field():
class CreateBorrowForm(forms.ModelForm):
book_isbn = forms.CharField()
class Meta:
model = Borrow
fields = ['book_isbn']
def save(self, commit=True):
instance = super().save(commit=False)
book_isbn = self.cleaned_data['book_isbn']
try:
book = Book.objects.get(isbn=book_isbn, available=True)
instance.book = book
except Book.DoesNotExist:
raise ValidationError('Book does not exist or it is unavailable')
if commit:
instance.save()
return instance

Perfom Django Validation for field that is NOT part of form

I would like to raise a ValidationError based on one of the fields in my Django model, without having the respective filed as part of a ModelForm. What I found after googling a bit is the concept of validators for models. So I tried to do the following:
def minimumDuration(value):
if value == 0:
raise ValidationError("Minimum value accepted is 1 second!")
class PlaylistItem(models.Model):
position = models.IntegerField(null=False)
content = models.ForeignKey(Content, null=True, on_delete=models.SET_NULL)
item_duration = models.IntegerField(validators = [minimumDuration], default = 5, null=True, blank=True)
playlist = models.ForeignKey(Playlist, null=True, on_delete=models.CASCADE)
However, no error appears when I introduce 0 in the respective field. From Django's documentation I found out that validators are not automatically applied when saving a model. It redirected me to this page, but I don't really understand how to apply those. Any idea?
Here is an example of a form with such a custom field outside of the Model:
class ExampleForm(forms.ModelForm):
custom_field = forms.BooleanField(
label='Just non model field, replace with the type you need',
required=False
)
class Meta:
model = YourModel
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# optional: further customize field widget
self.fields['custom_field'].widget.attrs.update({
'id': self.instance.pk + '-custom_field',
'class': 'custom-field-class'
})
self.fields['custom_field'].initial = self._get_custom_initial()
def _get_custom_initial(self):
# compute initial value based on self.instance and other logic
return True
def _valid_custom_field(value):
# validate your value here
# return Boolean
def clean(self):
"""
The important method: override clean to hook your validation
"""
super().clean()
custom_field_val = self.cleaned_data.get('custom_field')
if not self._valid_custom_field(custom_field_val):
raise ValidationError(
'Custom Field is not valid')

Stuck on linking ManytoMany relationships with Modelform

I'm pretty new to Django and I am working on a project that currently requires the following:
I have two basic structures: a Project model and a TeamMember model- both related to each other through a ManytoMany relationship. Then I have an TMAssigned 'through' class. The team member will have many projects assigned to it over time.
I have a ModelFrom which creates a Project model through the creation of the form.
My question is, How do I link the team member to the newly created project upon the submission of the form?
Here is a bit of my model & form code:
TeamMember
class TeamMember(models.Model):
firstname = models.CharField(max_length=100, default= "First Name")
lastname = models.CharField(max_length=100, default= "Last Name")
fullname = models.CharField(max_length=100, default= "Full Name")
email = models.EmailField(max_length=254)
cellphone = PhoneNumberField(null=False, blank=False, unique=True)
numberofcases = models.IntegerField(max_length=10000, default=0)
#property
def fullnamefunc(self):
fullname = "{} {}".format(self.firstname, self.lastname)
return fullname
def __str__(self):
return self.fullname
Project
class Project(models.Model):
pursuitname = models.CharField(max_length=500)
datecreated = models.DateTimeField(auto_now=True)
bdmember = models.ManyToManyField('team.TeamMember')
Views.py
class bdFormView(TemplateView):
template_name = os.path.join(BASE_DIR, "templates/masterform/bdform.html")
def get(self,request):
form = bdForm()
return render (request, self.template_name, {'form': form})
def post(self, request):
form = bdForm(request.POST)
if form.is_valid():
print("form is valid")
project = form.save(commit=False)
project.save()
text = form.cleaned_data['briefcard']
Form.py
class bdForm(forms.ModelForm):
bdmemberlist = TeamMember.objects.all().order_by('lastname')
pursuitname = forms.CharField()
bdmember = forms.ModelChoiceField(queryset= bdmemberlist)
addbdteam = forms.ModelMultipleChoiceField(
queryset=TeamMember.objects.all().order_by('lastname'), widget=Select2MultipleWidget, required=False)
class Meta:
model = Project
fields = ['pursuitname','addbdteam','bdmember',]
def __init__(self, *args, **kwargs):
if kwargs.get('instance'):
initial = kwargs.setdefault('initial', {})
initial['projects'] = [t.pk for t in
kwargs['instance'].project_set.all()]
forms.ModelForm.__init__(self, *args, **kwargs)
def save(self, commit=True):
instance = forms.ModelForm.save(self, False)
old_save_m2m = self.save_m2m
def save_m2m():
old_save_m2m()
for project in self.cleaned_data['bdmember']:
instance.teammember_set.add(project)
Thanks in advance!!
Edit- after doing some more research, I've removed the "Through" model from the script and am trying to rely on the form.py save method to do the join. However, when I do this- the two are still not linking up properly.
Since only your admin (superusers?) will log in, you can start off by using the in-built Django Admin.
I would recommend this for you, at least for now, because you're a beginner and the Admin Form is stunningly simple to use. Then, you can create a custom form later on when you're more comfortable. :-)
With this in mind, you can try eliminating the 'through' table (you may need to reset your migrations), and try this.
Admin.py
from django.contrib import admin
from .models import TeamMember, TMAssigned, Project,
TeamMembersInLine(admin.TabularInline):
model = TeamMember
extra = 1
#admin.register(Project):
class ProjectAdmin(admin.ModelAdmin):
list_display = ('pursuitname', 'bdmember ', 'datecreated')
inlines = [TeamMembersInLine]
Here's another answer that delves into the through table. It was asked by someone in your situation and the answer is relevant too.

Can't edit but can add new inline in Django admin

Here are my models
class Note():
note = models.TextField(null=False, blank=False, editable=True)
user = models.ForeignKey(to=User, null=True, blank=True)
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey("content_type", "object_id")
And an inline I created this model to incorporate in any admin is below
class NoteInline(GenericTabularInline):
model = Note
extra = 0
What I need here is, I want to see all the current notes but don't want the logged in user to edit them. At the moment user can edit old and add new. So here is what I did,
class NoteInline(GenericTabularInline):
model = Note
extra = 0
def get_readonly_fields(self, request, obj=None):
if obj and 'change' in request.resolver_match.url_name:
return ['note', 'user', ]
else:
return []
But now if user adds new note he sees a disabled (not editable) note text ares. However user can see old fields not editable.
How to implement this functionality?
I am having the same inquiry.
However, I do not care if the fields in the inline are "read only" or not. I just do not want them changed once they are created.
For this purpose, I created a NoteForm in forms.py which raises a validation error if the instance has changed while it has initial data:
class NoteForm(forms.ModelForm):
def clean(self):
if self.has_changed() and self.initial:
raise ValidationError(
'You cannot change this inline',
code='Forbidden'
)
return super().clean()
class Meta(object):
model = Note
fields='__all__'
admin.py:
class NoteInline(GenericTabularInline):
model = Note
extra = 0
form = NoteForm

Django:What is wrong with my code?

These are my models:
class war(models.Model):
title = models.CharField(max_length=20)
class option(models.Model):
warval =models.ForeignKey(war)
value = models.CharField(max_length=10)
class warform(ModelForm):
class Meta:
model = war
class option_form(ModelForm):
class Meta:
model = option
exclude = ('warval')
And this is the view which handles creation of option:
def warhandler(request,war_id):
f=modelformset_factory(option,form=option_form)
wobj=get_object_or_404(war,pk=war_id)
if request.method == 'POST':
formset = f(request.POST,queryset=option.objects.filter(warval=wobj))
if formset.is_valid():
formset.save()
return HttpResponse("Saved!check your model")
else:
return render_to_response("formset.html",RequestContext(request,{"set":formset}))
else:
formset = f(queryset=option.objects.filter(warval=wobj))
print(formset)
return render_to_response("formset.html",RequestContext(request,{"set":formset,"war":wobj}))
So when I submit the form to this view,I get the following error:
Exception Type: IntegrityError
Exception Value: hit_option.warval_id may not be NULL
I know what this error is and why it is coming but how can I remove this error?
As you realize, this is because you're not setting the foreign key value anywhere. You probably want to use inlineformset_factory which will take care of setting the FK to the parent war object for you.
Make warval a non-required field with
class option(models.Model):
warval =models.ForeignKey(war, null=True)
value = models.CharField(max_length=10)
or define formset.warval before saving, make a default value...
edit:
read here and here for using subset of fields on a form and saving.

Categories

Resources