django- createview with url parameter shows error - python

I have a view AsignacionGeneral from where I call the view assignacion_contact with href="{% url 'gestionAsignacion:assignacion_contact' pk=asig.id %} and pass the parameter by url which is the primary key to be inserted later in the foreign key idasignacion_general = models.ForeignKey('AsignacionGeneral', models.DO_NOTHING, db_column='idasignacion_general') , the view assignacion_contact has a form, it opens fine, I fill the form but when I send it I want it to stay in the same view.
example: I am in the url "assignation/assignationContact/1", the pk=1 , I take that value and assign it to the Foreign Key, I fill the form with the data but when I send it it redirects me to 'assignation/assignationContact' which does not exist and I want it to stay in the url ' 'asignacion/asignacionContact/1'
error image
url.py
app_name = 'gestionAsignacion'
urlpatterns = [
path('asignacionContact/<int:pk>',AsignacionCreateView.as_view(), name='asignacion_contact'),
path('detalle/asignacion/<int:pk>',descargarAsignacion.as_view(), name='descargar_asignacion'),
path('asignacionGeneral/',asignacionGeneral.as_view(), name='asignacion_general'),
]
view.py
class AsignacionCreateView(CreateView):
model=AsignacionContact
form_class=AsignacionForm
template_name='gestionAsignacion/asignacion_contact.html'
# success_url = reverse_lazy('gestionAsignacion:asignacion_general')
def get_initial(self):
# call super if needed
return {'idasignacion_general': self.kwargs['pk']}
def form_valid(self, form):
print('hola form_valid')
isvalid = super().form_valid(form)
return isvalid
def get_success_url(self):
return HttpResponseRedirect(reverse('gestionAsignacion:asignacion_general'))
def get_context_data(self, **kwargs):
# Llame primero a la implementación base para obtener un contexto
context = super().get_context_data(**kwargs)
# Agregar un QuerySet de todos los libros
context['asig_general'] = AsignacionGeneral.objects.get(id=self.kwargs['pk'])
context['object_list'] = AsignacionContact.objects.filter(idasignacion_general=self.kwargs['pk'])
return context
model.py
class AsignacionContact(models.Model):
id = models.IntegerField(primary_key=True)
idasignacion_general = models.ForeignKey('AsignacionGeneral', models.DO_NOTHING, db_column='idasignacion_general')
nombre_asignacion = models.CharField(max_length=100, blank=True, null=True)
fecha_inicio = models.DateField(blank=True, null=True)
fecha_fin = models.DateField(blank=True, null=True)
observacion = models.CharField(max_length=255, blank=True, null=True)
def __str__(self):
return self.nombre_asignacion
class Meta:
managed = False
db_table = 'asignacion_contact'
form.py
class AsignacionForm(ModelForm):
def __init__(self, *args, **kwargs):
super(AsignacionForm, self).__init__(*args, **kwargs)
for field in iter(self.fields):
self.fields[field].widget.attrs.update({
'class': 'form-control'
})
class Meta:
model=AsignacionContact
fields=['idasignacion_general','nombre_asignacion','fecha_inicio','fecha_fin','observacion']

This path('asignacionContact/<int:pk>',... means that the primary key of the object is required to show the view.
For example, an object with pk=1 would work for the URL path asignacion/asignacionContact/1.
You don't have a view that's just asignacion/asignacionContact/ in your urls.py.
That's why you are getting a Page not found (404).
I think you think <int:pk> in urls.py are GET querystrings. They are not. They are required URL values. GET parameters appear appended to the URL for example if you were to submit a form with a method="GET". The form data would appear in the URL.

Related

Django AssertionError when testing Create View

I'm running some tests for my app 'ads', but when I try to test the CreateView it fails with the following message:
AssertionError: 'just a test' != 'New title'
Here's the test:
class AdTests(TestCase):
def setUp(self):
self.user = get_user_model().objects.create_user(
username='test_user',
email='test#email.com',
password='secret'
)
self.ad = Ad.objects.create(
title='just a test',
text='Ehy',
owner=self.user
)
def test_ad_create_view(self):
response = self.client.post(reverse('ads:ad_create'), {
'title': 'New title',
'text': 'New text',
'owner': self.user.id,
})
self.assertEqual(response.status_code, 302)
self.assertEqual(Ad.objects.last().title, 'New title')
self.assertEqual(Ad.objects.last().text, 'New text')
So it could be that the test fails in creating a new ad, and then it compares the fields with the first ad in the setUp method.
I upload the rest of the code if it can help:
urls.py
from django.urls import path, reverse_lazy
from . import views
app_name='ads'
urlpatterns = [
path('', views.AdListView.as_view(), name='all'),
path('ad/<int:pk>', views.AdDetailView.as_view(), name='ad_detail'),
path('ad/create',
views.AdCreateView.as_view(success_url=reverse_lazy('ads:all')), name='ad_create'),
...
]
models.py
class Ad(models.Model) :
title = models.CharField(
max_length=200,
validators=[MinLengthValidator(2, "Title must be greater than 2 characters")]
)
price = models.DecimalField(max_digits=7, decimal_places=2, null=True)
text = models.TextField()
"""We use AUTH_USER_MODEL (which has a default value if it is not specified in settings.py) to create a Foreign Key relationship between the Ad model
and a django built-in User model"""
owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
comments = models.ManyToManyField(settings.AUTH_USER_MODEL, through='Comment', related_name='comments_owned')
picture = models.BinaryField(null=True, editable=True)
tags = TaggableManager(blank=True)
content_type = models.CharField(max_length=256, null=True, help_text='The MIMEType of the file')
favorites = models.ManyToManyField(settings.AUTH_USER_MODEL, through='Fav', related_name='favorite_ads')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
views.py
class AdCreateView(LoginRequiredMixin, View):
template_name = 'ads/ad_form.html'
success_url = reverse_lazy('ads:all')
def get(self, request, pk=None):
form = CreateForm()
ctx = {'form': form}
return render(request, self.template_name, ctx)
# Pull data
def post(self, request, pk=None):
form = CreateForm(request.POST, request.FILES or None)
if not form.is_valid():
ctx = {'form': form}
return render(request, self.template_name, ctx)
pic = form.save(commit=False)
pic.owner = self.request.user
pic.save()
form.save_m2m()
return redirect(self.success_url)
forms.py (the view uses it especially to check and save the image)
class CreateForm(forms.ModelForm):
max_upload_limit = 2 * 1024 * 1024
max_upload_limit_text = naturalsize(max_upload_limit)
# Call this 'picture' so it gets copied from the form to the in-memory model
# It will not be the "bytes", it will be the "InMemoryUploadedFile"
# because we need to pull out things like content_type
picture = forms.FileField(required=False, label='File to Upload <= ' + max_upload_limit_text)
upload_field_name = 'picture'
class Meta:
model = Ad
fields = ['title', 'text', 'price', 'picture', 'tags']
# Check if the size of the picture is less than the one specified (see above).
def clean(self):
cleaned_data = super().clean()
pic = cleaned_data.get('picture')
if pic is None:
return
if len(pic) > self.max_upload_limit:
self.add_error('picture', "File must be < " + self.max_upload_limit_text + " bytes")
# Convert uploaded File object to a picture
def save(self, commit=True):
instance = super(CreateForm, self).save(commit=False)
# We only need to adjust picture if it is a freshly uploaded file
f = instance.picture # Make a copy
if isinstance(f, InMemoryUploadedFile): # Extract data from the form to the model
bytearr = f.read()
instance.content_type = f.content_type
instance.picture = bytearr # Overwrite with the actual image data
if commit:
instance.save()
self.save_m2m()
return instance
I hope it is useful, thanks in advance!
According to Django Doc
Create View:
A view that displays a form for creating an object, redisplaying the form with validation errors (if there are any) and saving the object.
This is not a Valid way to do create View based on my experience. Check the doc Doc here.
if i understand what you are talking about You want to submit Ad Model using Create View, if you want to submit it in form You can something like this:
from django.views.generic import CreateView
from django.contrib.auth.mixins import LoginRequiredMixin
from django.urls import reverse_lazy,
class PostCreativeView(LoginRequiredMixin, CreateView):
model = #Your Model
fields = [#Fields of the model You want to submit]
template_name = #html template you want to submit the form
success_url = reverse_lazy(#url for redirected user when the form is submitted)
def form_valid(self, form):
form.instance.user = self.request.user
return super (PostCreativeView, self).form_valid(form)
in the form template you can add:
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Save">
</form>
for styling you can follow this: Answer

I have an error msg trying to use a slug field that contains an unusual character

My DB (MariaDB) is set to utf8mb4, my model and my view are all defined to use unicode characters.
The creation of the record is ok and a slug is correctly created with a value as for example corée. The problem raise right after the creation with a NoReverse match error msg. I had a look to the documentation but I don't understand what I'm doing wrong. Thanks for your support.
NoReverseMatch at /masterdat/countrycode/
Reverse for 'countrycode_update' with keyword arguments '{'slug': 'corée'}' not found. 1 pattern(s) tried: ['masterdat/countrycode/(?P[-a-zA-Z0-9_]+)/$']
Request Method: GET
Request URL: http://127.0.0.1:8000/masterdat/countrycode/
Django Version: 3.2.5
Exception Type: NoReverseMatch
Exception Value:
Reverse for 'countrycode_update' with keyword arguments '{'slug': 'corée'}' not found. 1 pattern(s) tried: ['masterdat/countrycode/(?P[-a-zA-Z0-9_]+)/$']
URL:
from django.urls import path
from masterdat import views
from masterdat.views import CountrycodeListView
from masterdat.views import CountrycodeCreateView, CountrycodeUpdateView
urlpatterns = [
path('masterdathome/', views.masterdathome, name='masterdathome'),
path('countrycode/', CountrycodeListView.as_view(),
name='countrycode_list'),
path('countrycode/add/', CountrycodeCreateView.as_view(),
name='countrycode_add'),
path('countrycode/<slug:slug>/', CountrycodeUpdateView.as_view(),
name='countrycode_update'),
]
Model:
class Countrycode(models.Model):
cou_code = models.CharField(
"Country name", max_length=40, primary_key=True)
cou_recblocked = models.BooleanField("Country record blocked")
cou_creator = models.ForeignKey(
User, on_delete=models.PROTECT, related_name='+',
verbose_name="Created by")
cou_creationdate = models.DateTimeField("Creation date",
auto_now=True)
cou_modifier = models.ForeignKey(
User, on_delete=models.PROTECT, related_name='+',
verbose_name="Last modification by")
cou_modified = models.DateTimeField(
"Last modification date", auto_now=True)
slug = models.SlugField(max_length=50, allow_unicode=True)
class Meta:
ordering = ["cou_code"]
def __str__(self):
return self.cou_code
def get_absolute_url(self):
return reverse('countrycode_update', kwargs={'slug': self.slug})
View:
from masterdat.models import Countrycode
#-----------------------
# List view Management
#-----------------------
class CountrycodeListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
permission_required = 'masterdat.view_countrycode'
model = Countrycode
paginate_by = 10
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
return context
# Search bar
def get_queryset(self):
result = super(CountrycodeListView, self).get_queryset()
query = self.request.GET.get('q')
if query:
query_list = query.split()
result = result.filter(
reduce(operator.and_,
(Q(cou_code__icontains=q) for q in query_list))
)
return result
#-----------------------------------
# Create and Update Views Management
#-----------------------------------
from masterdat.forms import CountrycodeForm
# Create View -----------
class CountrycodeCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
permission_required = 'masterdat.add_countrycode'
model = Countrycode
form_class = CountrycodeForm
success_url = reverse_lazy('countrycode_list')
def form_valid(self, form):
form.instance.slug = slugify(form.instance.cou_code, allow_unicode=True)
form.instance.cou_creator = self.request.user
form.instance.cou_modifier = self.request.user
form.save
return super().form_valid(form)
# Update View -----------
class CountrycodeUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
permission_required = 'masterdat.change_countrycode'
model = Countrycode
form_class = CountrycodeForm
success_url = reverse_lazy('countrycode_list')
def form_valid(self, form):
form.instance.cou_modifier = self.request.user
form.save
return super().form_valid(form)
The regular expression (?P[-a-zA-Z0-9_]+) will only match "cor" because "é" is not a member of the range "A-Z" or "a-z".
You could change it to (?P[-\w0-9_]+) to match non-ASCII characters.

Use URL to set model as true or false

I have a todo APP and I would like to have a link where the user can click and set the "todo" as complete without deleting it from my database.
I use CBV but cannot figure out how do it :
I tried
views.py :
class TodoDeleteView(LoginRequiredMixin, DeleteView):
model = Todo
success_url = '/'
template_name = 'dashboard/dashboard_confirm_delete.html'
def completeTodo(request, todo_id):
todo = Todo.objects.get(pk=todo_id)
todo.complete = True
todo.save()
But it delete it from my DB and it does not set it to true.
My models.py
class Todo(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE,verbose_name="Nom de l'utilisateur")
text = models.CharField(max_length=150, verbose_name="Nom de la Todo")
complete = models.BooleanField(default=False)
You define a DeleteView, and deleting the object, is just part of the delete control flow. In order to change the behavior, we can override the delete function, like:
class TodoDeleteView(LoginRequiredMixin, DeleteView):
model = Todo
pk_url_kwarg = 'todo_id'
success_url = '/'
template_name = 'dashboard/dashboard_confirm_delete.html'
def delete(self, request, *args, **kwargs):
self.object = self.get_object()
success_url = self.get_success_url()
self.object.complete = True
self.object.save()
return HttpResponseRedirect(success_url)
The pk_url_kwarg is necessary to use this to obtain the primary key to filter and retrieve the correct object with get_object().
The reason why we might want to use a DeleteView, is because people can make a DELETE request on that endpoint.

Dynamically alter form fields in views.py file of django

I was wondering if there is a way that I can alter a model form within the views.py file to create a multiple choice dropdown field for form choices. I want to set each option on the choice field from the results of a queryset.
for example:
I want to from_acct field to have a scroll down option with the following list..
wells fargo
chase
tabz
bank of america
the list of banks are results of a query set
Here is what i have so far in the views.py file.
form = TransferForm()
form.fields['from_acct'].queryset = Accounts.objects.filter(user = currentUser).all()
message = 'please fill out the below form'
parameters = {
'form':form,
'currentUser':currentUser,
'message':message,
}
return render(request, 'tabs/user_balance.html', parameters)
here is the forms.py file
class TransferForm(forms.ModelForm):
class Meta:
model = Transfers
fields = ['from_acct', 'to_acct', 'amount', 'memo']
labels = {
'from_acct':'from',
'to_acct':'to',
}
here is the model.py file
class Transfers(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
from_acct = models.CharField(max_length=150, default='account')
to_acct = models.CharField(max_length=150, default='accont')
amount = models.DecimalField(decimal_places=2, max_digits=9, default=0)
memo = models.CharField(max_length=200, default='memo')
frequency = models.SmallIntegerField(default=1)
status = models.SmallIntegerField(default=1)
create = models.DateTimeField(auto_now_add=True)
You can try to set choices arg for CharField by function.
Like that:
class Transfers(models.Model):
field = models.CharField(max_length=255, choices=result_query())
def result_query(self):
# you can use that with self if u need transfers.pk for querying
return Something.objects.exclude(bank_id__in=[bank.id for bank in self.banks.all())
def result_query():
# or there if not
return Something.objects.filter(any_field__gte=123)
For sure, you can realize any logic in the function, so you can dynamically change options for char field.
UPDATE:
Sure, u haven't pass request into the function.
That should be like that:
view.py:
def my_view(request):
if request.method == 'GET':
form = TransferForm(user=request.user)
...
return something here
forms.py
class TransferForm(ModelForm):
class Meta:
model = Transfer
def __init__(self, *args, **kwargs):
user = kwargs.pop('user')
super(TransferForm, self).__init__(*args, **kwargs)
self.fields['accounts'].choices = Accounts.objects.filter(user = currentUser).all()

Need help creating a model in a view

models.py
class Match(models.Model):
match_name = models.CharField(max_length=100)
player = models.CharField(max_length=100, choices=match_game, default=2)
time_start = models.DateTimeField(blank=True, default=None, null=True)
match_finished = models.BooleanField(default=False)
def get_absolute_url(self):
return reverse('match:details', kwargs={'pk': self.pk})
def __str__(self):
return self.match_name
class PlayerSignup(models.Model):
current_player = models.ForeignKey(User)
signup = models.ForeignKey(Match)
urls.py
url(r'^create/add/$', views.MatchCreate.as_view(), name='match-add'),
url(r'^(?P<pk>[0-9]+)/$', views.DetailView.as_view(template_name = 'match/bracket_detail.html'), name='details'),
url(r'^search/$', views.IndexView.as_view(template_name = 'match/bracket.html'), name='search'),
url(r'(?P<pk>[0-9]+)/$', views.PlayerSign, name='join')
views.py
def PlayerSign(request):
model = PlayerSignup.objects.all()
match = Match.objects.get(pk=Match.pk)
joinmatch = PlayerSignup(current_player=request.user, signup=match)
joinmatch.save()
return render(request, 'match/bracket_detail.html', {'model': model })
template
Join Match
when a person clicks on the 'Join Match' link i would like it to create a PlayerSignup model and link it to the current match that they are on.
when i click the Join Match link nothing happens, no new model, no error
First, try to edit this statement
def PlayerSign(request):
...
match = Match.objects.get(pk=Match.pk)
to
def PlayerSign(request, pk):
...
match = Match.objects.get(pk=pk)
Because there is an request parameter in URL named pk, you should pass this parameter to the query method.
Second, review your url define
url(r'^(?P<pk>[0-9]+)/$', views.DetailView.as_view(template_name = 'match/bracket_detail.html'), name='details'),
url(r'(?P<pk>[0-9]+)/$', views.PlayerSign, name='join')
Change to
url(r'^match_detail/(?P<pk>[0-9]+)/$', views.DetailView.as_view(template_name = 'match/bracket_detail.html'), name='details'),
url(r'^player_detail/(?P<pk>[0-9]+)/$', views.PlayerSign, name='join')

Categories