Differentiate object vs instance - python

Some consider model as an object, some consider as an instance. Can anyone tell me what is the difference between these two examples?
model.py:
class ToDo(models.Model):
name = models.CharField(max_length=100)
due_date = models.DateField()
def __str__(self):
return self.name
forms.py:
class ToDoForm(forms.ModelForm):
class Meta:
model = ToDo
fields = ['name', 'due_date']
views.py:
def todo_list(request):
todos = ToDo.objects.all()
context = {'todo_list': todos}
return render(request, 'todoApp/todo_list.html', context)
Considering the code below, what is form instance?
class PostDetailView(DetailView):
model = Post
def post(self, *args, **kwargs):
form = CommentForm(self.request.POST)
if form.is_valid():
post = self.get_object()
comment = form.instance
comment.user = self.request.user
comment.post = post
comment.save()
return redirect('detail', slug=post.slug)
return redirect('detail', slug=self.get_object().slug)

So in object-oriented programming, an object is an instance of a class. So model instance and model object are the same.
Let's do an example for that:
# This is your class
class ToDo(models.Model):
name = models.CharField(max_length=100)
due_date = models.DateField()
# If somewhere I call
my_var = ToDo() # my_var contain an object or an instance of my model ToDo
As for your question about the form, each form in Django may or may not contain an instance. This instance is the object modified by the form. When you create an empty form, this form.instance is None, because your form is not bound to an object. But if you build a form taking an object to modify as its parameter or after filling it, this object is the instance.
Example:
form = CommentForm()
print(form.instance) # This will return None, there is no instance bound to the form
comment = Comment.objects.get(pk=1)
form2 = CommentForm(instance=comment)
print(form2.instance) # Now the instance contain an object Comment or said an other way, an instance of Comment. When you display your form, the fields will be filled with the value of this instance
I hope that it's a little bit more clear.

CommentForm is ModelForm and ModelForm has instance attribute (which you can set (update scenario) or __init__ method of CommentForm will instantiate new model instance of model that you set to Metaclass
from BaseModelForm source:
if instance is None:
# if we didn't get an instance, instantiate a new one
self.instance = opts.model()
object_data = {}

Related

django built-in class based view - Where is self.object from?

In the following code, I don't understand something about the object.
class SingleObjectMixin(ContextMixin):
"""
Provide the ability to retrieve a single object for further manipulation.
"""
model = None
queryset = None
slug_field = 'slug'
context_object_name = None
slug_url_kwarg = 'slug'
pk_url_kwarg = 'pk'
query_pk_and_slug = False
...
...
def get_context_data(self, **kwargs):
"""Insert the single object into the context dict."""
context = {}
if self.object:
context['object'] = self.object
context_object_name = self.get_context_object_name(self.object)
if context_object_name:
context[context_object_name] = self.object
context.update(kwargs)
return super().get_context_data(**context)
There is an attribute self.object'in the method get_context_data. I would like to figure out where the attribute `object' is created from.
SingleObjectMixin is a mixin normally used by views. E.g. BaseDetailView uses this mixin where its method get sets the object:
https://github.com/django/django/blob/c8eb9a7c451f7935a9eaafbb195acf2aa9fa867d/django/views/generic/detail.py#L108
The following website gives you a great overview and description of all class based views and some mixins:
https://ccbv.co.uk/projects/Django/4.1/django.views.generic.detail/BaseDetailView/

How do you modify form data before saving it while using Django's CreateView?

I'm using the CreateView of Django and I'm trying to find out how I can modify any text which gets sent before it gets saved. For example, right now I'm only looking to lowercase all the text before saving.
I know I need to use form_valid() but I can't seem to get it right.
forms.py
class ConfigForm(forms.ModelForm):
class Meta:
model = Config
fields = ["heading", "name", "data", "rating"]
views.py
def form_valid(self, form):
super().form_valid(form)
form.fields["heading"].lower()
form.fields["name"].lower()
form.fields["data"].lower()
That shouldn't be done in form_valid. You should do that in the form itself. Instead of letting CreateView automatically create a form for you, do it explicitly and overwrite the clean method.
class MyForm(forms.ModelForm):
class Meta:
model = MyModel
fields = ('list', 'of', 'fields')
def clean(self):
for field, value in self.cleaned_data.items():
self.cleaned_data['field'] = value.lower()
...
class MyCreateView(views.CreateView):
form_class = MyForm
Override get_form_kwargs method to update the kwargs which instantiates the form.
Solution:
def get_form_kwargs(self):
# update super call if python < 3
form_kwargs = super().get_form_kwargs()
form_kwargs['data']['str_field_name'] = form_kwargs['data']['str_field_name'].lower()
return form_kwargs
Ref:
get_form_kwargs docs
While it may not be the nicest solution, it can be done like this:
def form_valid(self, form):
self.object = form.save(commit=False)
# ...
self.object.save()
return http.HttpResponseRedirect(self.get_success_url())
Just for the record
In the first case
def get_form_kwargs(self):
# update super call if python < 3
form_kwargs = super().get_form_kwargs()
form_kwargs['data']['str_field_name'] = form_kwargs['data'['str_field_name'].lower()
return form_kwargs
Django complains "This QueryDict instance is immutable". And workaround is
data = kwargs['data'].copy() # mutable copy
data['foo'] = 'whatever' #supply the missing default value
kwargs['data'] = data

How to use extra field from modelForms to query an object? Django

I have the following form:
class Recipe_IngredientForm(forms.ModelForm):
class Meta:
model = Recipe_Ingredient
fields = ('quantity', 'quantityUnit')
def __init__(self, *args, **kwargs):
super(Recipe_IngredientForm, self ).__init__(*args, **kwargs)
self.fields['ingredient_form'] = forms.CharField()
And I'm trying to get the value of this form to search for an object, if it exists, i'll set it to be saved in my model.
def recipe_add_ingredient(request, pk):
recipe = get_object_or_404(Recipe, pk=pk)
if request.method == "POST":
form = Recipe_IngredientForm(request.POST)
if form.is_valid():
recipeIngredient = form.save(commit=False)
recipeIngredient.recipe = recipe
aux = form.fields['ingredient_form']
recipeIngredient.ingredient = Ingredient.objects.get(name=aux)
recipeIngredient.save()
return redirect('recipe_detail', pk=recipe.pk)
else:
form = Recipe_IngredientForm()
return render(request, 'recipe/recipe_add_ingredient.html', {'form': form})
But I get an error when submitting the form: Ingredient matching query does not exist, but it shows that I'm getting a value that exists via GET, and if I query the exact same thing in the shell, it return my object. Any Idea?
You should be accessing cleaned_data, not fields.
aux = form.cleaned_data['ingredient_form']
Also note, you should define that field at class level, then you don't need to define __init__ at all.
class Recipe_IngredientForm(forms.ModelForm):
ingredient_form = forms.CharField()
class Meta:
model = Recipe_Ingredient
fields = ('quantity', 'quantityUnit')

How get instance in a method clean from some forms

How can I get the Article instance in my model form's clean method? I tried too access self.instance but it is None. How do I get the previous field values?
model
class Article(models.Model):
name = models.CharField(max_length=25)
value = models.CharField(max_length=25)
forms
class ArticleForm(forms.ModelForm)
class Meta:
model = Article
fields = '__all__'
def clean(self):
cleaned_data = super().clean()
get_instance = self.instance
print(get_instance) and I get None
views
def test(request)
form = ArticleForm({'name':'test', 'value':'test'})
if form.is_valid():
print(1)
else:
print(form.errors)
You get None because you didn't instantiate the form with an instance.
form = ArticleForm({'name':'test', 'value':'test'})
If you instantiate the form with an instance, then you can access it with self.instance in the clean method.
article = Article.objects.get(pk=1)
form = ArticleForm({'name':'test', 'value':'test'}, instance=article)
However, note that cleaning the form alters the model instance. If you want the original values, you should refetch the instance from the database, e.g. original_instance = Art
def clean(self):
cleaned_data = super().clean()
if self.instance is not None and self.instance.pk is not None:
original_instance = Article.objects.get(pk=self.instance.pk)
else:
original_instance = None
...
If you only want to know which fields changed, and don't care about their original values, it would be simpler to use the changed_data attribute.

Django - Problems with get_or_create()

I'm facing problems using get_or_create() in my view.
What I want to do is have the User get or create an instance of the Keyword model whenever he wants to add a keyword.
I have a Keyword model that looks like this:
class Keyword(models.Model):
word = models.CharField(max_length=30, unique=True, default=None)
members = models.ManyToManyField(settings.AUTH_USER_MODEL, blank=True, default=None)
def __str__(self):
return self.keywords
I have a form to create the keyword:
class KeywordForm(forms.ModelForm):
keywords = forms.CharField(max_length=30)
def __init__(self, *args, **kwargs):
super(KeywordForm, self).__init__(*args, **kwargs)
self.fields["keywords"].unique = False
class Meta:
fields = ("keywords",)
model = models.Keyword
I've tried different things in the view and here is my current version, without the use of get_or_create. It only creates the keyword:
class KeywordCreationView(LoginRequiredMixin, generic.CreateView):
form_class = forms.KeywordForm
model = models.Keyword
page_title = 'Add a new keyword'
success_url = reverse_lazy("home")
template_name = "accounts/add_keyword.html"
def form_valid(self, form):
var = super(KeywordCreationView, self).form_valid(form)
self.object.user = self.request.user
self.object.save()
self.object.members.add(self.object.user)
return var
How should my view look in order to get the keyword if it exists and if it does, add the User as 'member'. If it doesn't, create the Keyword.
Thanks for your help!
I do believe CreateView isn't the right class for this. You should use UpdateView instead and override the get_object method (which is actually a part of the SingleObjectMixin) ancestor of this class based view.
The source code of this mixin is rather daunging but in your case something as simple as
def get_object(self, queryset=None):
pk = self.kwargs.get(self.pk_url_kwarg)
if queryset:
obj, c = queryset.get_or_create(pk=pk)
else:
obj, c = MyModel.get_or_create(pk=pk)
return obj
might work. But frankly, it's alot simpler to use a simple (non class based view)

Categories

Resources