I created a django form (IssueForm) which is meant to be used to register an object which is instance of one of my models (Issue). Following are the model:
model.py
class Issue(models.Model):
TYPE_FIELDS = [
("Math", "Math"),
("Physics", "Physics"),
("Programming", "Programming"),
("Arts", "Arts")
]
issue_text = models.TextField(default="Please insert text")
issue_description = models.TextField(default="Newly created")
issue_deadline = models.DateField()
issue_field = models.CharField(max_length=30, choices=TYPE_FIELDS)
published_by = models.ForeignKey(User, on_delete=models.CASCADE, default=None)
def __str__(self):
return self.issue_description
the form used:
forms.py
class IssueForm(forms.ModelForm):
def __init__(self, user, *args, **kwargs):
self.user = user
super(IssueForm, self).__init__(*args, **kwargs)
TYPE_FIELDS = [
("Math", "Math"),
("Physics", "Physics"),
("Programming", "Programming"),
("Arts", "Arts")
]
issue_text = forms.CharField(widget=forms.Textarea, required=True)
issue_description = forms.CharField(widget=forms.Textarea, required=True)
issue_deadline = forms.DateField(required=True)
issue_fields = forms.ChoiceField(choices=TYPE_FIELDS, required=True)
class Meta:
model = Issue
fields = [
'issue_text',
'issue_description',
'issue_deadline',
'issue_fields'
]
def save(self, commit=True):
issue = super(IssueForm, self).save(commit=False)
issue.issue_text = self.cleaned_data['issue_text']
issue.issue_description = self.cleaned_data['issue_description']
issue.issue_deadline = self.cleaned_data['issue_deadline']
issue.issue_fields = self.cleaned_data['issue_fields']
if commit:
issue.published_by = self.user
issue.save()
return issue
and the related view:
views.py
def create_issue(request):
if ExtendedUser.objects.filter(user=request.user).exists():
if request.method == 'POST':
form = IssueForm(request.user, request.POST)
if form.is_valid():
form.save()
return redirect("/issues")
else:
form = IssueForm(request.user)
args = {'form': form}
return render(request, "issues/create_issue.html", args)
else:
raise Http404("You are not allowed to perform this action")
The forms works for every field in the model, they are all registered right, except for issue_fields. If i try giving a default value to the field in the model, that is the value that is saved on the database, otherwise I just get an empty field. Also I believe the problem comes from the form used, because if i try to create a new issue from the django admin interface it works just fine.
I feel like it's one of those silly mistakes, but I'm just starting with django and python in general and cannot figure it out on my own.
Thank you for your time!!
The field on your model is called issue_field, but you set issue_fields.
Note that also you are doing far more work here than necessary. Your save method completely duplicates what the superclass does already; you should remove all that code except for the setting of the user value.
enter code hereIf you want to use Choices, you haven't to write one more time list of choices in your forms.py file.
This is an example :
#In your models.py file
LIST_CHOICE = (('A','A'), ('B','B'))
class Test(models.Model) :
foo = models.CharField(choices=LIST_CHOICE, verbose_name="foo")
and
#In your form.py file
TestForm(forms.Modelform) :
class Meta :
model = Test
fields = ['foo']
It's not necessary to overwrite LIST_CHOICE in your form file ;)
So, dont touch to your model.py file, but in your form.py file, just write :
class IssueForm(forms.ModelForm):
issue_text = forms.CharField(widget=forms.Textarea)
issue_description = forms.CharField(widget=forms.Textarea)
def __init__(self, user, *args, **kwargs):
self.user = user
super(IssueForm, self).__init__(*args, **kwargs)
class Meta:
model = Issue
fields = [
'issue_text',
'issue_description',
'issue_deadline',
'issue_fields'
]
Don't forget to remove s in issue_field ;)
Related
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.
I have got the code from https://github.com/adandan01/mybook, the code is working fine, even when I have updated it to Django 2. It's very simple project for adding a person in a form, and his/her relatives in the inline form. Everything works but when I add a relative name and forget to add his relationship, and submitted the form, unfortunately, that record will not pass the validation but will give no error messages as well. Django will ignore the entire record. For example, the record for Hawra in the image, will not be saved and Django will remove it. For this simple App there are only two fields to be filled (name and relationship), but I'm working on app with 8 fields, and it will be difficult to lose the data. is there any way to make django do the validation in the formset/subform as long as any fields have data and will ask the user to fill all required fields?
models.py:
class Profile(models.Model):
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
created_date = models.DateTimeField(default=timezone.now)
def get_absolute_url(self):
return reverse('profile-update', kwargs={'pk': self.pk})
def __unicode__(self):
return "%s %s" % (self.first_name, self.last_name)
class FamilyMember(models.Model):
profile = models.ForeignKey(Profile, on_delete=models.PROTECT)
name = models.CharField(max_length=100)
relationship = models.CharField(max_length=100)
form.py
class ProfileForm(ModelForm):
class Meta:
model = Profile
exclude = ()
class FamilyMemberForm(ModelForm):
class Meta:
model = FamilyMember
exclude = ()
FamilyMemberFormSet = inlineformset_factory(Profile, FamilyMember,
form=FamilyMemberForm, extra=1)
views.py
class ProfileCreate(CreateView):
model = Profile
fields = ['first_name', 'last_name']
class ProfileFamilyMemberCreate(CreateView):
model = Profile
fields = ['first_name', 'last_name']
success_url = reverse_lazy('profile-list')
def get_context_data(self, **kwargs):
data = super(ProfileFamilyMemberCreate, self).get_context_data(**kwargs)
if self.request.POST:
data['familymembers'] = FamilyMemberFormSet(self.request.POST)
else:
data['familymembers'] = FamilyMemberFormSet()
return data
def form_valid(self, form):
context = self.get_context_data()
familymembers = context['familymembers']
with transaction.atomic():
self.object = form.save()
if familymembers.is_valid():
familymembers.instance = self.object
familymembers.save()
return super(ProfileFamilyMemberCreate, self).form_valid(form)
I found the solution here django inline_formset - form.empty_permitted = False doesn't work
I had to add the following code before if (familymembers.is_valid():...) in the create and update class, so, now Django will show the error if I entered data in the Name field only and will tell me the Relationship field is required.
if familymembers.is_valid() == False:
return self.render_to_response(self.get_context_data(form=form,familymembers=familymembers ))
I am using django admin tabular-inline admin interface.
model.py
class Issues(TimeStampedModel):
issue_owner = models.ForeignKey(USER_MODEL, related_name='issue_owner')
issue_no = models.CharField(max_length = 500, null = True, blank = True)
class IssueComments(TimeStampedModel):
comment_owner = models.ForeignKey(USER_MODEL, related_name='comment_owner')
issue = models.ForeignKey(Issues, null=True, blank=True)
comment = models.TextField(null=True, blank=True)
I am trying to use tabular-inline in admin
admin.py
class IssueCommentsAdmin(admin.TabularInline):
model = IssueComments
extra = 1
def formfield_for_foreignkey(self, db_field, request=None,**kwargs):
field = super(IssueCommentsAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
if db_field.name == 'comment_owner':
if request.user is not None:
field.queryset = field.queryset.filter(username = request.user.username)
if not field.queryset:
field.queryset = field.queryset.all()
else:
field.queryset = field.queryset.none()
return field
class IssuesAdmin(admin.ModelAdmin):
model = Issues
list_display = ('issue_no', 'title', 'owner_phone_number', 'status', 'issue_priority', 'classification')
inlines = [ IssueCommentsAdmin ]
def render_change_form(self, request, context, *args, **kwargs):
context['adminform'].form.fields['assigned_to_user'].queryset = User.objects.filter(is_staff=True)
return super(IssuesAdmin, self).render_change_form(request, context, args, kwargs)
i want to restrict the comment_owner to logged in user only in choice-field. I am able to do that also but issue i am facing here is for the comments whiche already have the comment_owner i want to keep that as it is.
Here comments which have comment_owner are not getting pre selected.
A little late but, for anyone
You should create another Model Admin class in admin.py and give permissions for this object. I recommend you change your classnames.
class IssueCommentsInline(admin.TabularInline):
...
class IssueCommentsAdmin(admin.ModelAdmin):
...
admin.site.register(IssueComments,IssueCommentsAdmin)
In this way, you can edit all data with get_queryset and field data with get_field_queryset.
regards!
I did a lot of search for an issue I am facing, but couldn't find a suitable solution. I am a Django beginner
I am creating a project in which an User will be able to ask a wish, and other users will be assigned that wish, which they can then draw and submit.
I created views for asking and getting a wish, but facing issue while submitting the sketch for the wish. I do not know how to show only those wishes in the add_sketch form for the current user and then update the sketch model with this new sketch.
Right now I am just using a charField for the uploaded sketch. Here is the code
models.py
class Wish(models.Model):
content = models.CharField(max_length=500)
wisher = models.ForeignKey(User)
created_on = models.DateTimeField(auto_now_add=True)
locked = models.BooleanField(default=False)
class Meta():
verbose_name_plural = 'Wishes'
def __unicode__(self):
return self.content
class Sketch(models.Model):
wish = models.ForeignKey(Wish)
sketcher = models.ForeignKey(User)
image_temp = models.CharField(max_length=128)
likes = models.IntegerField(default=0)
assigned_on = models.DateTimeField(auto_now_add=True)
submitted_on = models.DateTimeField(auto_now=True)
class Meta():
verbose_name_plural = 'Sketches'
def __unicode__(self):
return "Sketch for \""+ self.wish.content + "\""
views.py
#login_required
def add_sketch(request):
if request.method == "POST":
sketch_form = SketchForm(request.POST)
if sketch_form.is_valid():
add_sketch = sketch_form.save(commit=False)
add_sketch.save()
return sketchawish(request)
else:
print sketch_form.errors
else:
sketch_form = SketchForm()
return render(request, 'saw/add_sketch.html', {'sketch_form': sketch_form})
And here is the forms.py
class GetWishForm(forms.ModelForm):
wish = forms.ModelChoiceField(queryset= Wish.objects.filter(pk__in = Wish.objects.filter(locked=False)[:3].values_list('pk')), initial=0)
class Meta:
model = Sketch
fields = ('wish',)
class SketchForm(forms.ModelForm):
wish = forms.ModelChoiceField(queryset= Sketch.objects.all(), initial=0)
image_temp = forms.CharField(help_text='Imagine this is an upload button for image, write anything')
class Meta:
model = Sketch
fields = ('wish', 'image_temp')
UPDATE:
I edited the code according to #almalki's suggestion
forms.py
class SketchForm(forms.ModelForm):
wish = forms.ModelChoiceField(queryset= Sketch.objects.all(), initial=0)
image_temp = forms.CharField(help_text='Imagine this is an upload button for image, write anything')
def __init__(self, *args, **kwargs):
super(SketchForm, self).__init__(*args, **kwargs)
self.fields['wish'].queryset = kwargs.pop('wish_qs')
class Meta:
model = Sketch
fields = ('wish', 'image_temp')
views.py
#login_required
def add_sketch(request):
if request.method == "POST":
sketch_form = SketchForm(request.POST)
if sketch_form.is_valid():
add_sketch = sketch_form.save(commit=False)
add_sketch.save()
return sketchawish(request)
else:
print sketch_form.errors
else:
sketch_form = SketchForm(wish_qs=Wish.objects.filter(wisher=request.user))
return render(request, 'saw/add_sketch.html', {'sketch_form': sketch_form})
I still get the error init() got an unexpected keyword argument 'wish_qs'
UPDATE 2:
forms.py remains same as above, here is what I think the views.py should be
#login_required
def add_sketch(request):
if request.method == "POST":
sketch_form = SketchForm(request.POST, wish_qs=Sketch.objects.filter(sketcher=request.user))
if sketch_form.is_valid():
add_sketch = sketch_form.save(commit=False)
add_sketch.save()
return sketchawish(request)
else:
print sketch_form.errors
else:
sketch_form = SketchForm(wish_qs=Sketch.objects.filter(sketcher=request.user))
return render(request, 'saw/add_sketch.html', {'sketch_form': sketch_form})
When I choose a wish, and click submit, the error is: annot assign "": "Sketch.wish" must be a "Wish" instance.
I know this is because the model is expecting a Wish instance, but we are giving a Sketch instance, but I don't know how to achieve what I need. I think some change has to be made in the models.py, connecting Wish and Sketch reversibly.
You need to override the field query set in form initialization:
class SketchForm(forms.ModelForm):
wish = forms.ModelChoiceField(queryset= Sketch.objects.all(), initial=0)
image_temp = forms.CharField(help_text='Imagine this is an upload button for image, write anything')
def __init__(self, *args, **kwargs):
wish_qs = kwargs.pop('wish_qs')
super(SketchForm, self).__init__(*args, **kwargs)
self.fields['wish'].queryset = wish_qs
class Meta:
model = Sketch
fields = ('wish', 'image_temp')
And in your view, you need to pass a queryset filtered based on current logged in user:
sketch_form = SketchForm(request.POST, wish_qs=Wish.objects.filter(wisher=request.user))
and:
sketch_form = SketchForm(wish_qs=Wish.objects.filter(wisher=request.user))
I am trying to log the activities during save operation to track all the changes to user model. my approach is as follows.
class User(AbstractUser):
undergrad_college = models.CharField(max_length=20, choices=COLLEGE_CHOICES)
undergrad_degree = models.CharField(max_length=20, choices=COLLEGE_DEGREES)
postgrad_college = models.CharField(max_length=20, choices=COLLEGE_CHOICES)
postgrad_degree = models.CharField(max_length=20, choices=COLLEGE_DEGREES)
currently_working_on = models.TextField()
previous_work_experience = models.TextField()
previous_internship_experience = models.TextField()
def __str__(self):
return self.username
def save(self, *args, **kwargs):
Log(user=User, actions="Updated profile",
extra={"undergrad_college": self.undergrad_college,
"undergrad_degree": self.undergrad_degree,
"postgrad_college": self.postgrad_college,
"postgrad_degree": self.postgrad_degree,
"currently_working_on": self.currently_working_on,
"previous_work_experience": self.previous_work_experience,
"previous_internship_experience": self.previous_internship_experience
})
super(User, self).save(args, **kwargs)
my views are like this for handling the logging.
class ActivityMixin(LoginRequiredMixin):
def get_context_data(self, **kwargs):
context = super(ActivityMixin, self).get_context_data(**kwargs)
context['activities'] = Log.objects.filter(user=self.request.user)
return context
class IndexListView(ActivityMixin, ListView):
template_name = 'pages/home.html'
model = User
I get this error while performing the update action.
Cannot assign "<class 'users.models.User'>": "Log.user" must be a "User" instance.
Update view is as follows
class UserUpdateView(LoginRequiredMixin, UpdateView):
form_class = UserForm
# we already imported User in the view code above, remember?
model = User
# send the user back to their own page after a successful update
def get_success_url(self):
return reverse("users:detail",
kwargs={"username": self.request.user.username})
def get_object(self, **kwargs):
# Only get the User record for the user making the request
return User.objects.get(username=self.request.user.username)
How to assign the User model instance to the Log function. I cant get this working. I am Django newbie.
Looks like pretty straightforward, replace User with self:
Log(user=User, ...
Log(user=self, ...