I am working on a simple "issue tracking" web application as way to learn more about Django.
I am using Django 4.1.4 and Python 3.9.2.
I have the following classes in models.py (which may look familiar to people familiar with JIRA):
Components
Issues
IssueStates
IssueTypes
Priorities
Projects
Releases
Sprints
Originally I also had a Users class in models.py but now am trying to switch to using the Django User model. (The User class no longer exists in my models.py)
I have been studying the following pages to learn how best to migrate to using the Django Users model.
Django Best Practices: Referencing the User Model
Referencing the User Model
All of my List/Detail/Create/Delete view classes worked fine with all of the above models until I started working on using the Django User class.
-- models.py --
from django.conf import settings
class Issues(models.Model):
id = models.BigAutoField(primary_key=True)
project = models.ForeignKey(
to=Projects, on_delete=models.RESTRICT, blank=True, null=True
)
summary = models.CharField(max_length=80, blank=False, null=False, default="")
issue_type = models.ForeignKey(
to=IssueTypes, on_delete=models.RESTRICT, blank=True, null=True
)
issue_state = models.ForeignKey(
to=IssueStates, on_delete=models.RESTRICT, blank=True, null=True, default="New"
)
# https://learndjango.com/tutorials/django-best-practices-referencing-user-model
# https://docs.djangoproject.com/en/4.0/topics/auth/customizing/#referencing-the-user-model
reporter = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.RESTRICT,
related_name="reporter_id",
)
priority = models.ForeignKey(
to=Priorities, on_delete=models.RESTRICT, blank=True, null=True
)
component = models.ForeignKey(
to=Components, on_delete=models.RESTRICT, blank=True, null=True
)
description = models.TextField(blank=True, null=True)
planned_release = models.ForeignKey(
to=Releases, on_delete=models.RESTRICT, blank=True, null=True
)
# https://learndjango.com/tutorials/django-best-practices-referencing-user-model
# https://docs.djangoproject.com/en/4.0/topics/auth/customizing/#referencing-the-user-model
assignee = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.RESTRICT,
related_name="assignee_id",
)
slug = models.ForeignKey(
to="IssueSlugs", on_delete=models.RESTRICT, blank=True, null=True
)
sprint = models.ForeignKey(
to=Sprints, on_delete=models.RESTRICT, blank=True, null=True
)
def save(self, *args, **kwargs):
if not self.slug:
# generate slug for this new Issue
slug = IssueSlugs()
slug.project_id = self.project.id
slug.save()
self.slug = slug
super().save(*args, **kwargs)
def __str__(self):
return self.slug.__str__() + " - " + self.summary.__str__()
class Meta:
managed = True
db_table = "issues"
class IssueSlugs(models.Model):
"""
This table is used to generate unique identifiers for records in the
Issues table. My goal was to model the default behavior found in JIRA
where each Issue has a unique identifier that is a combination of:
1) the project abbreviation
2) a sequential number for the project
So here when creating a new Issue record, if it is the first record for
a particular project, the sequential number starts at 100, otherwise it
is the next sequential number for the project.
"""
id = models.BigAutoField(primary_key=True)
project = models.ForeignKey(
to=Projects, on_delete=models.RESTRICT, blank=True, null=True
)
slug_id = models.IntegerField(default=100)
slug = models.CharField(
max_length=80,
blank=False,
null=False,
unique=True,
)
def __str__(self):
return self.slug.__str__()
def save(self, *args, **kwargs):
if not self.slug:
result = IssueSlugs.objects.filter(
project_id__exact=self.project.id
).aggregate(Max("slug_id"))
# The first issue being created for the project
# {'slug_id__max': None}
if not result["slug_id__max"]:
self.slug_id = 100
self.slug = self.project.abbreviation + "-" + str(100)
else:
logging.debug(result)
next_slug_id = result["slug_id__max"] + 1
self.slug_id = next_slug_id
self.slug = self.project.abbreviation + "-" + str(next_slug_id)
super().save(*args, **kwargs)
class Meta:
managed = True
db_table = "issue_slugs"
-- issues.py --
class CreateUpdateIssueForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# save for IssueCreateView.form_valid()
self.kwargs = kwargs
font_size = "12pt"
for field_name in self.fields:
if field_name in ("summary", "description"):
self.fields[field_name].widget.attrs.update(
{
"size": self.fields[field_name].max_length,
"style": "font-size: {0}".format(font_size),
}
)
elif field_name in ("reporter", "assignee"):
# https://docs.djangoproject.com/en/4.0/topics/auth/customizing/#referencing-the-user-model
User = get_user_model()
choices = list()
choices.append(("", ""))
for element in [
{
"id": getattr(row, "id"),
"display": row.get_full_name(),
}
for row in User.objects.exclude(is_superuser__exact="t")
]:
choices.append((element["id"], element["display"]))
self.fields[field_name] = forms.fields.ChoiceField(
choices=choices,
# I had to specify required=False here to eliminate a very
# strange error:
# An invalid form control with name='assignee' is not focusable.
required=False,
)
else:
# all the <select> fields ...
self.fields[field_name].widget.attrs.update(
{
"class": ".my-select",
}
)
class Meta:
model = Issues
fields = [
"project",
"summary",
"component",
"description",
"issue_type",
"issue_state",
"reporter",
"priority",
"planned_release",
"assignee",
"sprint",
]
class IssueCreateView(LoginRequiredMixin, PermissionRequiredMixin, generic.CreateView):
"""
A view that displays a form for creating an object, redisplaying the form
with validation errors (if there are any) and saving the object.
https://docs.djangoproject.com/en/4.1/ref/class-based-views/generic-editing/#createview
"""
model = Issues
permission_required = "ui.add_{0}".format(model.__name__.lower())
template_name = "ui/issues/issue_create.html"
success_url = "/ui/issue_list"
form_class = CreateUpdateIssueForm
def form_valid(self, form):
User = get_user_model()
if "reporter" in self.kwargs:
form.instance.reporter = User.objects.get(id__exact=self.kwargs["reporter"])
if not form.is_valid():
messages.add_message(
self.request, messages.ERROR, "ERROR: '{0}'.".format(form.errors)
)
return super().form_valid(form)
action = self.request.POST["action"]
if action == "Cancel":
# https://docs.djangoproject.com/en/4.1/topics/http/shortcuts/#django.shortcuts.redirect
return redirect("/ui/issue_list")
return super().form_valid(form)
def get_initial(self):
"""
When creating a new Issue I'm setting default values for a few
fields on the Create Issue page.
"""
# https://docs.djangoproject.com/en/4.0/topics/auth/customizing/#referencing-the-user-model
User = get_user_model()
from ui.models import IssueStates, Priorities, IssueTypes
issue_state = IssueStates.objects.get(state__exact="New")
priority = Priorities.objects.get(priority__exact="Medium")
issue_type = IssueTypes.objects.get(issue_type__exact="Task")
reporter = User.objects.get(username__exact=self.request.user)
return {
"issue_state": issue_state.id,
"priority": priority.id,
"issue_type": issue_type.id,
"reporter": reporter.id,
}
When I try to create a new Issue, the "new Issue" form is displayed normally, but when I save the form I get a Django error with a stack trace I don't understand because it does not have a reference to any of my code, so I have no idea where to start debugging.
16:22:48 ERROR Internal Server Error: /ui/issue/create
Traceback (most recent call last):
File "/Users/a0r470/git/issue_tracker/env/lib/python3.9/site-packages/django/core/handlers/exception.py", line 55, in inner
response = get_response(request)
File "/Users/a0r470/git/issue_tracker/env/lib/python3.9/site-packages/django/core/handlers/base.py", line 197, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/Users/a0r470/git/issue_tracker/env/lib/python3.9/site-packages/django/views/generic/base.py", line 103, in view
return self.dispatch(request, *args, **kwargs)
File "/Users/a0r470/git/issue_tracker/env/lib/python3.9/site-packages/django/contrib/auth/mixins.py", line 73, in dispatch
return super().dispatch(request, *args, **kwargs)
File "/Users/a0r470/git/issue_tracker/env/lib/python3.9/site-packages/django/contrib/auth/mixins.py", line 109, in dispatch
return super().dispatch(request, *args, **kwargs)
File "/Users/a0r470/git/issue_tracker/env/lib/python3.9/site-packages/django/views/generic/base.py", line 142, in dispatch
return handler(request, *args, **kwargs)
File "/Users/a0r470/git/issue_tracker/env/lib/python3.9/site-packages/django/views/generic/edit.py", line 184, in post
return super().post(request, *args, **kwargs)
File "/Users/a0r470/git/issue_tracker/env/lib/python3.9/site-packages/django/views/generic/edit.py", line 152, in post
if form.is_valid():
File "/Users/a0r470/git/issue_tracker/env/lib/python3.9/site-packages/django/forms/forms.py", line 205, in is_valid
return self.is_bound and not self.errors
File "/Users/a0r470/git/issue_tracker/env/lib/python3.9/site-packages/django/forms/forms.py", line 200, in errors
self.full_clean()
File "/Users/a0r470/git/issue_tracker/env/lib/python3.9/site-packages/django/forms/forms.py", line 439, in full_clean
self._post_clean()
File "/Users/a0r470/git/issue_tracker/env/lib/python3.9/site-packages/django/forms/models.py", line 485, in _post_clean
self.instance = construct_instance(
File "/Users/a0r470/git/issue_tracker/env/lib/python3.9/site-packages/django/forms/models.py", line 82, in construct_instance
f.save_form_data(instance, cleaned_data[f.name])
File "/Users/a0r470/git/issue_tracker/env/lib/python3.9/site-packages/django/db/models/fields/__init__.py", line 1006, in save_form_data
setattr(instance, self.name, data)
File "/Users/a0r470/git/issue_tracker/env/lib/python3.9/site-packages/django/db/models/fields/related_descriptors.py", line 237, in __set__
raise ValueError(
ValueError: Cannot assign "'2'": "Issues.reporter" must be a "User" instance.
[27/Dec/2022 16:22:48] "POST /ui/issue/create HTTP/1.1" 500 120153
Generally I understand that under the covers, Django creates two fields in the Issues model for me:
reporter
reporter_id
and I understand that the reporter field needs to contain a User instance instead of an integer (2). BUT I don't know WHERE in my code I should do this assignment.
I have tried overriding a few methods in my CreateUpdateIssueForm and IssueCreateView as a way to try to find where my code is causing problems - no luck so far.
In my IssueCreateView(generic.CreateView) class, I added the following to my form_valid() method, intending to retrieve the correct User record and assign it to form.instance.reporter, but the code appears to be failing before it gets to my form_valid() method.
def form_valid(self, form):
User = get_user_model()
if "reporter" in self.kwargs:
form.instance.reporter = User.objects.get(id__exact=self.kwargs["reporter"])
Clearly I do not fully understand the flow of control in these Generic View classes.
Thank you for any help you can provide!
I discovered that trying to migrate my own Users model to a CustomUser model is a non-trivial undertaking! I learned this from Will Vincent and his excellent post on this very topic!
Django Best Practices: Custom User Model
The Django documentation also states that migrating to the Django User in the midst of an existing project is non-trivial.
Changing to a custom user model mid-project
So, to solve my problem I started with a new empty project with only the CustomUser in my models.py as Mr. Vincent described, which worked perfectly.
After that, I setup the rest of my model classes in models.py, referencing the CustomUser model as needed.
assignee = models.ForeignKey(
to=CustomUser,
on_delete=models.RESTRICT,
blank=True,
null=True,
)
And copied the rest of my template files, view source files, static files, etc. from my original project into this new project.
My codebase is now working as expected using the Django User model.
Huge Thanks to Mr. Will Vincent's excellent article on this issue!
Related
halo i'm working on a project that requires user to give a feedback whenever possible using django rest_framework
but im getting an some difficulties doing that
below is my code snippet & err msg
##mode file
class Review(models.Model):
school = models.ForeignKey(
Profile, on_delete=models.CASCADE, related_name='review')
name = models.CharField(max_length=250, blank=True, null=True)
reviewer_email = models.EmailField()
rating = models.CharField(
max_length=250, blank=True, null=True)
review = models.TextField()
##serializer file
class ReviewSerializer(serializers.ModelSerializer):
class Meta:
model = Review
fields = ('name', 'review', 'id', 'reviewer_email', 'rating')
def perform_create(self, serializer):
id = self.request
print(self.request.user.profile)
serializer.save(school=self.request.user.profile.school_id)
##apiView
class ReviewAPIView(generics.CreateAPIView):
serializer_class = ReviewSerializer
permissions = [permissions.AllowAny]
queryset = Review.objects.all()
err msg
File "/home/olaneat/Desktop/files/project/django/schMrk/lib/python3.8/site-packages/rest_framework/views.py", line 509, in dispatch
response = self.handle_exception(exc)
File "/home/olaneat/Desktop/files/project/django/schMrk/lib/python3.8/site-packages/rest_framework/views.py", line 469, in handle_exception
self.raise_uncaught_exception(exc)
File "/home/olaneat/Desktop/files/project/django/schMrk/lib/python3.8/site-packages/rest_framework/views.py", line 480, in raise_uncaught_exception
raise exc
File "/home/olaneat/Desktop/files/project/django/schMrk/lib/python3.8/site-packages/rest_framework/views.py", line 506, in dispatch
response = handler(request, *args, **kwargs)
File "/home/olaneat/Desktop/files/project/django/schMrk/lib/python3.8/site-packages/rest_framework/generics.py", line 190, in post
return self.create(request, *args, **kwargs)
File "/home/olaneat/Desktop/files/project/django/schMrk/lib/python3.8/site-packages/rest_framework/mixins.py", line 19, in create
self.perform_create(serializer)
File "/home/olaneat/Desktop/files/project/django/schMrk/sch-market/schoolDetail/apiviews.py", line 213, in perform_create
print(self.request.user.profile)
AttributeError: 'AnonymousUser' object has no attribute 'profile'
[04/Aug/2021 16:00:59] "POST /school-detail/add-review HTTP/1.1" 500 105554
can anyone help pls
You didn't share the request body.
My guess(from the error) is that you didn't specify the id of the school object it should be related to
You should limit access for non-authorized users. Otherwise, self.request.user could be an AnonymousUser object, which has no a profile relation.
permissions = [permissions.IsAuthenticated]
Can someone help me out please.
I want to redirect to a post detail page after deleting a comment of a given pk.
I got an AttributeError when ever I hit the comment delete button.
Error: function object has no attribute 'format'
If I change my success_url of the comment delete view to redirect to post list page with no argument, it works well but that's not what I want. I need the user to be redirected to post detail page after deleting a comment.
Here's my Post model
class Post(models.Model):
STATUS_CHOICES = (
('draft', 'Draft'),
('published', 'Published'),
)
title = models.CharField(max_length=250)
slug = models.SlugField(max_length=250, unique_for_date='publish')
author = models.ForeignKey(get_user_model(), on_delete=models.CASCADE, related_name='blog_posts')
body = models.TextField()
publish = models.DateTimeField(default=timezone.now)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='published')
objects = models.Manager()
published = PublishedManager()
tags = TaggableManager()
class Meta:
ordering = ('-publish',)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post_detail', args=[self.pk, self.slug])
def save(self, *args, **kwargs):
self.slug = slugify(self.title)
super().save(*args, **kwargs)'''
Comment model
class Comment(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments')
author = models.ForeignKey(get_user_model(), on_delete=models.CASCADE,)
comment = models.TextField()
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
active = models.BooleanField(default=True)
class Meta:
ordering = ('created',)
def __str__(self):
return f'Comment by {self.author} on {self.post}'
def get_absolute_url(self):
return self.post.get_absolute_url() + f'#comment-{self.pk}'
CommentDeleteView
class CommentDeleteView(DeleteView):
model = Comment
template_name = 'comment_delete.html'
def success_url(self):
post = Post.objects.get(pk=self.object.post.pk)
return post.get_absolute_url()
And the full error
Internal Server Error: /blog/13/comment_delete/
Traceback (most recent call last):
File "C:\Users\SUNNY\blog_folder\ven\lib\site-packages\django\core\handlers\exception.py", line 34, in inner
response = get_response(request)
File "C:\Users\SUNNY\blog_folder\ven\lib\site-packages\django\core\handlers\base.py", line 115, in _get_response
response = self.process_exception_by_middleware(e, request)
File "C:\Users\SUNNY\blog_folder\ven\lib\site-packages\django\core\handlers\base.py", line 113, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "C:\Users\SUNNY\blog_folder\ven\lib\site-packages\django\views\generic\base.py", line 71, in view
return self.dispatch(request, *args, **kwargs)
File "C:\Users\SUNNY\blog_folder\ven\lib\site-packages\django\views\generic\base.py", line 97, in dispatch
return handler(request, *args, **kwargs)
File "C:\Users\SUNNY\blog_folder\ven\lib\site-packages\django\views\generic\edit.py", line 218, in post
return self.delete(request, *args, **kwargs)
File "C:\Users\SUNNY\blog_folder\ven\lib\site-packages\django\views\generic\edit.py", line 212, in delete
success_url = self.get_success_url()
File "C:\Users\SUNNY\blog_folder\ven\lib\site-packages\django\views\generic\edit.py", line 222, in get_success_url
return self.success_url.format(**self.object.__dict__)
AttributeError: 'function' object has no attribute 'format'
[19/Jun/2020 11:50:28] "POST /blog/13/comment_delete/ HTTP/1.1" 500 88418
To configure the DeleteView success URL, you have to override the method get_success_url(). See that the method where you are implementing the success URL doesn't match that name, instead, its super class is trying to access the success_url property in the default get_success_url(), giving you that error.
I am building a notification system for a company, where admin users can create Projects and add users to them. The Project model has 9 attributes but I only want to show 3 or 4 fields when a Project is created, but show them all when an existing Project is updated.
This change will only need to be reflected on the Django admin site, so I have extended the ProjectAdmin with my own ProjectForm, where I extend the init method to check if it is a new instance and if so remove certain fields.
# models.py
class Project(models.Model):
project_number = models.IntegerField()
name = models.CharField(max_length=100)
permit = models.CharField(max_length=100, blank=True, default='')
is_active = models.BooleanField(default=True)
users = models.ManyToManyField(CustomUser, blank=True, related_name='project_users')
# add a default
levels = models.ManyToManyField('Level', blank=True, related_name='project_levels')
total_contract_hours = models.IntegerField(default=0, blank=True, verbose_name='Total Design Hours')
hours_used = models.IntegerField(default=0, blank=True, verbose_name='Total Design Hours Used')
notes = models.ManyToManyField('notes.ProjectNote', related_name='core_project_notes', blank=True)
history = HistoricalRecords()
def __str__(self):
ret_str = "{} {}".format(self.project_number, self.name)
if self.permit:
ret_str += " | Permit: {}".format(self.permit)
return ret_str
# admin.py
class ProjectForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(ProjectForm, self).__init__(*args, **kwargs)
attrs = {'class': 'form-control', 'required': True}
if self.instance and self.instance.pk is None:
# creating project
exclude = ['is_active', 'users', 'levels', 'hours_used', 'notes']
for field in exclude:
try:
del self.fields[field]
except ValueError:
print('{} does not exist'.format(field))
for field in self.fields.values():
field.widget.attrs = attrs
class Meta:
model = Project
fields = ['project_number', 'name', 'total_contract_hours']
class ProjectAdmin(admin.ModelAdmin):
form = ProjectForm
fields = ['project_number', 'name', 'permit', 'is_active', 'users', 'levels', 'total_contract_hours', 'hours_used', 'notes']
As I stated I only want basic Project fields on creation, but show all attributed when updating existing Project. With just these changes, I now get a KeyError:
KeyError: "Key 'is_active' not found in 'ProjectForm'. Choices are:
name, permit, project_number, total_contract_hours."
However, when I print the available fields it returns an OrderedDict with all of the model attributes as keys. What am I doing wrong? Thanks!
I figured it out, the field must be in listed in Meta and then you just set the field to be a hidden field.
class ProjectForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(ProjectForm, self).__init__(*args, **kwargs)
print("Adding project")
if not self.instance or self.instance.pk is None:
for name, field in self.fields.items():
if name in ['design_manager', ]:
field.widget = forms.HiddenInput()
class Meta:
model = Project
fields = ['project_number', 'name', 'design_manager', 'total_contract_hours']
class ProjectAdmin(admin.ModelAdmin):
form = ProjectForm
def save_model(self, request, obj, form, change):
obj.design_manager = request.user
super().save_model(request, obj, form, change)
I have a Files table with information about uploaded files in a remote directory. This is the model for that table:
class Files(models.Model):
id = models.AutoField(primary_key=True)
subjectid = models.ForeignKey('Subjects', models.DO_NOTHING, db_column='subjectid')
filetypeid = models.ForeignKey(FileTypes, models.DO_NOTHING, db_column='filetypeid')
filedescid = models.ForeignKey(FileDescription, models.DO_NOTHING, db_column='filedescid')
filepath = models.CharField(max_length=45, blank=True, null=True)
filename = models.FileField(upload_to='attachments/', blank=True, null=True)
ispublic = models.IntegerField(choices=YESNO)
extra_info = models.CharField(max_length=255, blank=True, null=True)
def __str__(self):
return self.filename.name or ''
class Meta:
managed = False
db_table = 'files'
verbose_name_plural = 'files'
I've created my own URL widget to replace the Django FileField url shown as 'Currently:' in the change_form template. The link points to a view that downloads the file. So far, so good it works but the problem is that when I try to add a new file I can select the new file with the Browse file button but when I click on Save the field filename field is empty and no file is uploaded.
class MyAdminURLFieldWidget(URLInput):
template_name = 'admin/widgets/url.html'
def __init__(self, attrs=None):
#final_attrs = {'class': 'vURLField'}
final_attrs = {'type': 'file'}
if attrs is not None:
final_attrs.update(attrs)
super(MyAdminURLFieldWidget, self).__init__(attrs=final_attrs)
def get_context(self, name, value, attrs):
context = super(MyAdminURLFieldWidget, self).get_context(name, value, attrs)
context['current_label'] = _('Currently:')
context['change_label'] = _('Change:')
context['widget']['href'] = smart_urlquote('/DownloadView/' + str(value.instance.id) + '/attachment/') if value else ''
return context
class FilesAdmin(admin.ModelAdmin):
list_display = ('id', '_animalid', '_filename', '_filedesc', 'ispublic', 'extra_info')
search_fields = ('subjectid__animalid','filename')
list_per_page = 50
def formfield_for_dbfield(self, db_field, **kwargs):
if db_field.name == 'filename':
request = kwargs.pop("request", None)
kwargs['widget'] = MyAdminURLFieldWidget
return db_field.formfield(**kwargs)
else:
return super(FilesAdmin, self).formfield_for_dbfield(db_field, **kwargs)
def _animalid(self, obj):
return obj.subjectid.animalid
def _filename(self, obj):
return obj.filename.name
def _filedesc(self, obj):
return obj.filedescid.description
Can anybody tell what I'm missing here?
Hi lost Django community,
I will answer my own question since it seems that nobody was able to realize the answer. It happens that as newbie I'm following examples I've found here and there but there is little about sub-classing the FileField associated widget. So, after diving deep into the Django code I've found the answer. In my case the problem was I derived my MyAdminURLFieldWidget from URLInput instead of the correct subclass ClearableFileInput.
You are welcome.
Recently I downloaded the Pinax project 0.7 to see what I can apply to my own project. Particularly, I ran Pinax and looked at the Bookmarks app that Pinax applied to itself. I copied the Bookmark apps and its dependencies to my own. However, Pinax runs on Django 1.0.4 and mine was 1.2.4 and there are some error in the form validation. Following are snippets from the bookmarks app:
class BookmarkInstance(models.Model):
bookmark = models.ForeignKey(Bookmark, related_name="saved_instances", verbose_name=_('bookmark'))
user = models.ForeignKey(User, related_name="saved_bookmarks", verbose_name=_('user'))
saved = models.DateTimeField(_('saved'), default=datetime.now)
description = models.CharField(_('description'), max_length=100)
note = models.TextField(_('note'), blank=True)
tags = TagField()
Form:
class BookmarkInstanceForm(forms.ModelForm):
url = forms.URLField(label = "URL", verify_exists=True, widget=forms.TextInput(attrs={"size": 40}))
description = forms.CharField(max_length=100, widget=forms.TextInput(attrs={"size": 40}))
redirect = forms.BooleanField(label="Redirect", required=False)
tags = TagField(label="Tags", required=False)
def __init__(self, user=None, *args, **kwargs):
self.user = user
super(BookmarkInstanceForm, self).__init__(*args, **kwargs)
# hack to order fields
self.fields.keyOrder = ['url', 'description', 'note', 'tags', 'redirect']
def clean(self):
if 'url' not in self.cleaned_data:
return
if BookmarkInstance.objects.filter(bookmark__url=self.cleaned_data['url'], user=self.user).count() > 0:
raise forms.ValidationError(_("You have already bookmarked this link."))
return self.cleaned_data
def save(self, commit=True):
self.instance.url = self.cleaned_data['url']
return super(BookmarkInstanceForm, self).save(commit)
class Meta:
model = BookmarkInstance
The params passed in were (<QueryDict: {u'url': [u'amazon.com'], u'note': [u'foo'], u'description': [u'bar'], u'tags': [u'']}>,) but did not cause a model validation error in 1.0.4. So, how can I make minimal adjustments in my view, possibly to skip the model validation to accommodate for this difference?