how to make URL pattern accept variable arguments in Django? - django

my title may not be very clear.
The problem I am facing now is:
my view passes arbitrary keyword arguments to another view.
This view handles with what info(keyword arguments it gets)
This is the example of search feature I am implementing.
Each user will have a list of Saved Searches. when they click on any one of the items in Search list, they will be directed to a different view that process the information it receives
In the Searches model, I have defined a get_absolute_url method that constructs the URL pattern of each of these search (based on models in the field).
my model:
class Searches(models.Model):
SELLER_CHOICES=(('OWNER','owner'),
('DEALER','dealer'),
('BOTH','both'), )
#search_id = models.IntegerField(primary_key=True)
user = models.ForeignKey(User)
make = models.CharField(max_length=100, blank=True)
model = models.CharField(max_length=100, blank=True)
keywords = models.CharField(max_length=100, blank=True)
max_price = models.IntegerField(blank=True, null=True)
min_price = models.IntegerField(blank=True, null=True)
max_year = models.IntegerField(blank=True, null=True)
min_year = models.IntegerField(blank=True, null=True)
pic_only = models.NullBooleanField()
search_title_only = models.NullBooleanField()
owner_dealer_all = models.CharField(max_length=10,choices=SELLER_CHOICES,verbose_name='owner/dealer')
class Meta:
#managed = False
db_table = 'Searches'
verbose_name_plural = "Searches"
def __unicode__(self):
return "%s %s %s-%s" %(self.make,self.model,self.max_year,self.min_year)
def get_absolute_url(self):
return reverse('postings.views.detail',args=[model_to_dict(self.object)])
view:
class SearchListView(ListView):
model=Searches
template:
{% extends "base.html" %}
{% block content %}
{% for obj in object_list %}
<p>{{ obj }}</p>
{% endfor %}
{% endblock %}
you can see from the image, when I click the your searches, I get error;
Reverse for 'postings.views.detail' with arguments '({'owner_dealer_all': u'DEALER', 'pic_only': True, 'make': u'toyota', u'id': 3, 'min_year': 1990, 'min_price': 4000, 'user': 1, 'keywords': u'hybrid', 'search_title_only': True, 'model': u'prius', 'max_price': 20000, 'max_year': 2012},)' and keyword arguments '{}' not found. 0 pattern(s) tried: []
Basically, I dont know how to handle this in the URL pattern.
OR
IF THIS IS A BAD DESIGN, PLEASE PLEASE SUGGEST A SOLUTION

If you really need to have all the variables in the URL, then you need to pass the variables as params of a GET query, so your URL will look like this:
http://example.com/search?owner_dealer_all=DEALER&pic_only=True&make=toyota&id=3&min_year=1990&min_price=4000&user=1&keywords=hybrid&search_title_only=True&model=prius&max_price=20000&max_year=2012
But, I would strongly suggest that you keep that URL as separate from the Search.get_absolute_url result, as the latter is supposed to point to the actual model. So have that function return something like:
http://example.com/search/23465
where the number is the ID for your Search model instance. In that view, get all the saved details and make a search on them. If you actually don't need the data in your URL, simply opt for the latter version only.

Related

Getting a TypeError while trying to make a unique page in Django

I'm making a picture gallery web-app. I want to make some of displayed photos to belong to a specific collection. Each collection is supposed to have its own page that displays all of the pictures that belong to it.
The name of each unique page is supposed to be photo_collection model, which I added to the class Image in models.py. But for some reason, I get TypeError at /projects/sample_collection_name/ - photo_collection() got an unexpected keyword argument 'photo_collection'
No idea what's causing this error. I tried renaming the photo_collection function (it has the same name as my model), but it didn't work.
models.py
class Image(models.Model):
title = models.CharField(max_length=300)
image = models.ImageField(null=True, blank=True, upload_to='images/')
pub_date = models.DateTimeField('Date published', default=timezone.now)
photo_collection = models.CharField('Photo collection', max_length=250, null=True, blank=True)
views.py
def photo_collection(request):
image = Image.objects.all()
return render (request, 'photo_app/collection.html', context={'image': image})
urls.py
urlpatterns = [
#some other patterns here
path('projects/<str:photo_collection>/', views.photo_collection, name='photo_collection'),
]
gallery.html
{% if i.photo_collection != Null %}
{{i.photo_collection}}
{% endif %}
you need to add photo_collection to your view parameters.
it will be like this:
def photo_collection(request, photo_collection):
image = Image.objects.all()
return render (request, 'photo_app/collection.html', context={'image': image})
when ever you add a variable to your url path you need to add that variable to view parameters too.
here is the documentation for this matter:
https://docs.djangoproject.com/en/4.1/topics/http/urls/

Django relationships between child and parent model

I am busy with a project where I have two models and I'm not sure how to do as I am fairly new to programming.
what I would like to do is have a template that
renders all the instances of first model
and then on the same template I want to have all the sales associated with each of those instances as well as the instances with different states(for example if an instance of first model is linked to 2 sales that has a state of "Confirmed" it should say 2 next to that instance name.)
class QAAgent(models.Model):
user=models.OneToOneField(User,on_delete=models.SET_NULL,null=True)
Qa_name = models.CharField(max_length=15)
def __str__(self):
return self.user.username
States = (('Pending',"Pending"),("Confirmed","Confirmed"),("Requested","Requested),("Cancelled","Cancelled"),("Not interested","Not interested"))
class Sale(models.Model):
QA = models.ForeignKey(QAAgent,on_delete=models.SET_NULL,blank=True,
null=True,related_name="sale")
State = models.CharField(choices=States,default="Pending",max_length=15)
Date_created = models.DateTimeField(auto_now_add=True)
Date_edited = models.DateTimeField(auto_now=True)
def__str__(self):
return self.client_name + " " + self.client_surname
I am not sure how to reply to answers but thanks guys that worked perfectly!
To list all your entries of the first model including it's related items you can do the following. Just be sure to pass the correct queryset to the view context.
{% for entry in qa_agent %}
{{ entry.qa_name}}
{{ entry.user.name }}
{% for sale in entry.qa_sale.all %}
{{sale.category.state }}
{% endfor %}
{% endfor %}
For your second request just create a method in your model class which filters for the desired status. I've called it get_confirmed here.
class QAAgent(models.Model):
user = models.OneToOneField(User, on_delete=models.SET_NULL, null=True)
qa_name = models.CharField(max_length=15) # You had Qa_name before
def get_confirmed(self):
return self.qa_sale.filter(state="Pending") # just write a method like this one for each of the other statuses.
def __str__(self):
return self.user.username
And then modify your template code so it uses this method to display the confirmed ones.
{% for entry in qa_agent %}
{{ entry.qa_name}}
{{ entry.user.name }}
{{ entry.get_confirmed.count }} # <-- like this
{% endfor %}
Just a quick side note about python code styling.
For variables and function names use snake_case. for classes use CamelCase.
So for your model properties it's best to only use lower case letters and separate words using an underscore. For classes start with an uppercase letter and separate words by using an uppercase letter for the first letter of the new word. This is how it should look like:
states = (
("Pending", "Pending"),
("Confirmed", "Confirmed"),
("Requested", "Requested"),
("Cancelled", "Cancelled"),
("Not interested", "Not interested"),
)
class Sale(models.Model):
qa = models.ForeignKey(QAAgent, on_delete=models.SET_NULL, blank=True, null=True, related_name="qa_sale")
state = models.CharField(choices=States, default="Pending", max_length=15)
date_created = models.DateTimeField(auto_now_add=True)
date_edited = models.DateTimeField(auto_now=True)
def __str__(self):
return f"{self.client_name} {self.client_surname}" # If you concatenate strings use an fstring like I did here. It's more readable

Can I have data from different fields of a model returned as separate HTML elements? (Django 2.1)

I am compiling a database of articles and have my model set up like this:
class articles(models.Model):
ArticleID = models.IntegerField(primary_key=True)
Title = models.CharField(max_length=500)
Author = models.CharField(max_length=200, null=True)
Journal = models.CharField(max_length=500, null=True)
Date = models.IntegerField(null=True)
Issue = models.IntegerField(null=True)
Link = models.URLField(max_length=800, null=True)
Content = models.TextField()
class Meta:
db_table = 'TEST'
def __str__(self):
return f'{self.Title}, {self.Author}, {self.Journal},{self.Date}, {self.Issue}, {self.Content}'
def get_absolute_url(self):
return reverse('article-detail', args=[str(self.ArticleID)])
The idea is pretty simple. Each meta data type (i.e. title, author) is it's own field, and the actual content of the article is in the field Content.
My view for this model:
def article_detail(request, ArticleID):
ArticleID = get_object_or_404(articles, ArticleID=ArticleID)
context = {'ArticleID': ArticleID}
return render(request, 'article_detail.html', context)
The HTML template for the view:
{% extends 'base.html' %}
{% block content %}
<div class="container">
{{ ArticleID }}
</div>
{% endblock %}
The data displayed in on the HTML page is one big block of text in one single HTML element. How can I make it so that I can use CSS to target each individual field from the model? Must I make separate models for each field (and bound them with foreign keys)?
No of course not. You can access fields with normal dot notation: ArticleID.Title, ArticleID.Author, etc.
(But you shouldn't call your context variable ArticleID; it's the whole article, not the ID. Also, Python style is to use lower_case_with_underscore for variables and attribute names.)
There is already a primary key in for every model which is called id, you don't have to explicitly declare that.
Secondly you are getting an article object with get_object_or_404 so if you use . (dot) notation you will get your desired value in your template.
Something like-
<h2>{{article.Title}}</h2>
<p>{{article.Content}}</p>
though you have to send article names instead of ArticleID in context variable.
In addition to Mr. Daniel Roseman's comment you should use class name Article instead of articles which is not pythonic.

Django display additional info for items in a list

I've a problem and i don't know the right way to solve it.
Basically,i've a mode, called Task:
class Task(models.Model):
#mto1: many task made of one template
template = models.ForeignKey(Template)
STATUS_CHOISE = (('PR', 'In process'), ('ST', 'Stopped'), ('FN', 'Finished'), ('DL', 'Deleted'),)
status = models.CharField(max_length=2, choices=STATUS_CHOISE, default='ST')
responses_required = models.IntegerField(default=0)
date_deadline = models.DateTimeField(auto_now=False, auto_now_add=False)
date_created = models.DateTimeField(auto_now_add=True, auto_now=False)
which can have several answers
class Response(models.Model):
#mto1: many Responses generated for one task
task = models.ForeignKey(Template)
STATUS_CHOISE = (('PR', 'Process'), ('FN', 'Finished'))
status = models.CharField(max_length=2, choices=STATUS_CHOISE, default='ST')
date_finished= models.DateTimeField(auto_now_add=False, auto_now=True)
date_created = models.DateTimeField(auto_now_add=True, auto_now=False)
ip_address=models.IPAddressField()
now, i would like to display all the task of one user (which comes from the template model, and this is done) with additional information about the answers, which are
- number of answer give (so which status is = FN)
- number of total answer (status FN or PR)
- and maybe some extra info.
i did this
#login_required
def TemplateList(request):
task_list = Task.objects.filter(user=request.user)
return render_to_response('task_list.html',{'task_list':task_list}, context_instance=RequestContext(request))
but this just display the data present in the model, what about the value i've to compute?
so far i did it with CustomTag, but it doesn't seem a clean solution. i would rather do the logic in the view and then use the template just for display results.
But: how can i add this value to each item of the Task List?
do i have to do like in this: Passing additional data to a template in Django so create the objects from scratch (which requires me some logic in the template as well since i've to match the value in the list with the objects i crate)? or is there any better solution?
i tried generic view ,like list_detail.object_list
in this way
def TaskListDetail(request):
return list_detail.object_list(
request,
queryset = Task.objects.filter(user=request.user),
template_name = 'task_list.html',
template_object_name = 'task_list',
)
but first, it does not show anything (while the other view shows data), second i don't know how to add extra data in a way that they are matched with the item of the list.
any suggestion?
You can add a method to your model.
class Task(models.Model):
#mto1: many task made of one template
template = models.ForeignKey(Template)
STATUS_CHOISE = (('PR', 'In process'), ('ST', 'Stopped'), ('FN', 'Finished'), ('DL', 'Deleted'),)
status = models.CharField(max_length=2, choices=STATUS_CHOISE, default='ST')
responses_required = models.IntegerField(default=0)
date_deadline = models.DateTimeField(auto_now=False, auto_now_add=False)
date_created = models.DateTimeField(auto_now_add=True, auto_now=False)
def do_some_calculation(self):
#make funny things
return "foo"
In your template, you can access this with:
{% for taks in task_list %}
{{ task.do_some_calculation }}
{% endfor %}

django many to many get attributes from other object

So I've been using django for a while now, and it's great. I've recently come across a little bit of a problem, and I'm sure there's a crappy way to get it to work, but what I've found with Django is that they've usually built in all sorts of mechanisms to do things for you. So what I'm not finding is this:
Here are my models:
class LandmarkGroup(models.Model):
Name = models.CharField(max_length=150)
Description = models.CharField(max_length=300, blank=True)
IsActive = models.BooleanField(default=True)
landmarks = models.ManyToManyField('Landmark', blank=True, null=True)
def __unicode__(self):
return self.Name
class Landmark(models.Model):
Name = models.CharField(max_length=150)
Description = models.CharField(max_length=300, blank=True)
Polygon = models.PolygonField()
IsActive = models.BooleanField(default=True)
objects = models.GeoManager()
def __unicode__(self):
return self.Name
I also have another model 'Team' that has a ManyToMany with LandmarkGroup, but I'm not going to post it here. I have a view where I query for all the landmarks that have a landmarkgroup that has a team with the same team id as the one I passed in:
def mobile_startup(request):
...
landmarkGroups = LandmarkGroup.objects.filter(team=device.team, IsActive=True)
landmarks = Landmark.objects.filter(landmarkgroup__team=device.team, IsActive=True)
...
return render_to_response('webservice/mobile_startup.html', {'landmarks': landmarks, 'landmarkGroups': landmarkGroups})
Everything works, the only problem I'm having is, I'm returning this all as JSON to the mobile app, and I want to provide the landmarkGroup id for the landmark, so in my template I've been trying to:
"landmarkGroup" : {{ landmark.landmarkgroup.id }} }
but that's not working. Does anyone know any way I can get the landmarkGroup ID for each landmark in my set? Do I need to extract it when I do the query? I know I can reference each landmarkGroup in the query because I can do 'landmarkgroup__team=device.team', but I need to able to reference this object in the template
LandmarkGroup.landmarksis a ManyToManyField,therefore one Landmark can belong to multiple groups.
You should be able to output them in your template like this:
{% for group in landmark.landmarkgroup_set.all %}{{ group.pk }}{% endfor %}
The first group belonging to the landmark should be accessible through {% landmark.landmarkgroup_set.all.0 %}