I've got 2 models
class Post(models.Model):
user = models.ForeignKey(User, verbose_name='User')
content = models.TextField(verbose_name='Text', max_length=4000)
date = models.DateTimeField(verbose_name='Date', default=now())
class Vote(models.Model):
vote = models.IntegerField(choices=VOTE, verbose_name='Vote')
post = models.ForeignKey(Post, verbose_name='Post')
user = models.ForeignKey(User, verbose_name='User')
and view which load last 15 posts:
posts = Post.objects.all().order_by('-date')[:15]
Now I want to have information about active user vote in all of my queryset objects
I thought about custom method in Post model which will check logged user and get vote objects:
def user_vote(self):
try:
data = Vote.objects.filter(post=self, user=request.user)
return data.vote
except ObjectDoesNotExist:
#not voted
But it seems django dont allow to use request in model.
Any other ideas?
You can pass user as argument to method like this:
def user_vote(self, user):
also I guess you need to get a Vote instance, not queryset, so you should user .get method instead .filter:
def user_vote(self, user):
try:
data = Vote.objects.get(post=self, user=user)
return data.vote
except ObjectDoesNotExist:
#not voted
then you can call this method with Post instance:
post.user_vote(request.user)
UPDATE
Django not allow to use method with arguments in template. So in your case better to refactor this method into templatetag:
def get_user_vote(post, user):
try:
data = Vote.objects.get(post=post, user=user)
return data.vote
except ObjectDoesNotExist:
#not voted
You can find manual here https://stackoverflow.com/a/6493001/3291969
don't forget to register it :)
That's not a django problem, it's because request isn't anywhere in the scope of your code. If you pass request to your model's method it will work. Something like:
def user_vote(self, request):
try:
data = Vote.objects.get(post=self, user=request.user)
return data.vote
except Vote.DoesNotExist:
#not voted
and call it like this with your Post instance:
# Note the request being passed as a parameter
vote = post.user_vote(request)
UPDATE:
To get all votes from a user you could do something like this:
votes = Vote.objects.filter(post=self, user=request.user)
return reduce(lambda x, y: x.vote + y.vote, votes, 0)
we set a 0 at the end, so in cases where there's no votes from this user, we'll get a 0 as a return.
This will sum every vote and return it.
If you don't want to sum these votes, change the line:
return reduce(lambda x, y: x.vote + y.vote, votes, 0)
to:
return map(lambda x, y: x.vote + y.vote, votes)
and this will get you a list with all the votes from the request.user user
I think you're looking for this package:
https://pypi.python.org/pypi/django-crequest/1.0
Description:
crequest will bring you current request object of your django application from anywhere in your code.
And it is used like this:
from crequest.middleware import CrequestMiddleware
current_request = CrequestMiddleware.get_request()
Related
I am trying to create a social media type site that will allow a user to follow and unfollow another user. followers has a ManyToManyField because a user can have many followers.
models.py
class Follower(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, default="")
followers = models.ManyToManyField(User, related_name="followers")
views.py
def username(request, user):
#get user
user = get_object_or_404(User.objects, username=user)
posts = Post.objects.filter(user=user).order_by('-date_and_time')
#follow button code
follow_or_unfollow = ''
try:
following = get_object_or_404(Follower, Q(
user=user) & Q(followers=request.user))
print(following)
except:
following = False
if following:
follow_or_unfollow = True
else:
follow_or_unfollow = False
if request.POST.get('follow'):
follower = Follower.objects.create(user=request.user)
follower.followers.add(*user)
follow_or_unfollow = False
elif request.POST.get('unfollow'):
follow_or_unfollow = True
#following.delete()
When it gets the 'follow' POST request, I want it to add the user who sent it (the one that is logged in) to be added to the followers. Right now, I am getting this error when I try to do that.
TypeError: django.db.models.fields.related_descriptors.create_forward_many_to_many_manager.<locals>.ManyRelatedManager.add() argument after * must be an iterable, not User
I know it says that it has to be iterable, but is there any way to just add one object at a time. Also, how would you delete this particular object?
The * in the arguments converts a list to individual args. For example-
lst = [1,2,3,4,5]
function(*lst)
can be just read as
function(1,2,3,4,5)
You have used follower.followers.add(*user). Hence, user must be an iterable to be unpacked and passed as a list or arguments. But user is a single User object.
You should just use follower.followers.add(user) in this case.
Alright, so I'm following a tutorial on creating a small questionnaire with Django.
A User can create a survey with different multiple choice questions. Since they're multiple choice questions, users can also set the options for the questions.
A "survey taker" can then start a survey, select his or her preferred questions and submit the form.
I would like to only show a single question per page so I'm trying to work with modelformset_factory and implement pagination using the build in paginator.
The form is rendered correctly and a user can submit an answer, however, the form fails before "formset.is_valid()", I just can't figure out why;
ValueError at /surveys/1/submit/4/
Cannot assign "'2'": "Answer.option" must be a "Option" instance.
So I can not save an integer and I somehow have to relate the integer with the id from the option model... But I can't access cleaned_data yet so I guess I'm missing something here. Did I forget something within the formset?
I've been staring at this for a while now so any help is appreciated.
#Views
def submit(request, survey_pk, sub_pk):
# Let's retrieve the survey which is created by the survey-taker
try:
survey = Survey.objects.prefetch_related("question_set__option_set").get(
pk=survey_pk, is_active=True
)
except Survey.DoesNotExist:
raise Http404()
try:
submission = survey.submission_set.get(pk=sub_pk, is_complete=False)
except Submission.DoesNotExist:
raise Http404()
# Retrieve all question associated with this survey
questions = survey.question_set.all()
# Get all related options from the questions
options = [q.option_set.all() for q in questions]
form_kwargs = {"empty_permitted": False, "options": options,}
# Setup a formset utilizing a ModelFormSet
AnswerFormset = modelformset_factory(
Answer,
form=AnswerModelForm,
formset=BaseAnswerFormSet,
exclude=(),
extra=len(questions),
)
if request.method == "POST":
print("Request.POST", request.POST)
# This is where the problem starts
formset = AnswerFormset(request.POST, form_kwargs=form_kwargs)
print("Formset: ", formset)
if formset.is_valid():
print("Formset is valid")
with transaction.atomic():
for form in formset:
Answer.objects.create(
option_id=form.cleaned_data["option"],
submission_id=sub_pk,
)
submission.is_complete = True
submission.save
return redirect("survey-thanks", pk=survey_pk)
else:
formset = AnswerFormset(form_kwargs=form_kwargs)
print(form_kwargs)
question_forms = zip(questions, formset)
return render(
request,
"survey/submit.html",
{
"survey": survey,
"question_forms": question_forms,
"formset": formset
}
)
#forms
# Setup a Modelformset
class AnswerModelForm(forms.ModelForm):
class Meta:
model = Answer
fields = "option",
def __init__(self, *args, **kwargs):
options = kwargs.pop("options")
# Options must be a list of Option objects
choices = {(o.pk, o.text) for o in options}
super().__init__(*args, **kwargs)
option_field = forms.ChoiceField(choices=choices, widget=forms.RadioSelect, required=True)
self.fields["option"] = option_field
class BaseAnswerFormSet(forms.BaseFormSet):
def get_form_kwargs(self, index):
kwargs = super().get_form_kwargs(index)
kwargs["options"] = kwargs["options"][index]
return kwargs
#models
class Answer(models.Model):
"""An answer a survey's questions."""
submission = models.ForeignKey(Submission, on_delete=models.CASCADE)
option = models.ForeignKey(Option, on_delete=models.CASCADE)
option_id=form.cleaned_data["option"],
is the suspect, it may require an option instance, even though you are trying to pass the id. Try changing option_id to option__id and explicitly setting the form.cleaned_data["option"] to int. Another way to to is to pull the option manually and pass it directly:
option = Option.object.get(id=int(form.cleaned_data["option"]))
I would like to compare a sentence which I input to my code with any value in database.
There is my function which apply data into database from my page:
def create_author(request):
form_author = AuthorForm()
context = {'form_author': form_author}
if request.method == 'POST':
form_author = AuthorForm(request.POST)
if form_author.is_valid():
name = request.POST.get('name',)
surname = request.POST.get('surname')
if name == Author.name and surname == Author.surname:
print("We have this author in database")
# There should be stop.
else:
author_object = Author(name=name, surname=surname)
author_object.save()
return render(request, 'authors/create_author.html', context)
and it works but it only adds - doesn't check if any author exist in db.
Any ideas for fixing it? Thanks in advice.
You can use exists().
Instead of this
if name == Author.name and surname == Author.surname:
print("We have this author in database")
Try this
if Author.objects.filter(name=name, surname=surname).exists():
print("We have this author in database")
You can use get_or_create [Django-doc] here, like:
def create_author(request):
form_author = AuthorForm()
context = {'form_author': form_author}
if request.method == 'POST':
form_author = AuthorForm(request.POST)
if form_author.is_valid():
name = form_author.cleaned_data['name']
surname = form_author.cleaned_data['surname']
__, created = Author.objects.get_or_create(name=name, surname=surname)
if not created:
print("We have this author in database")
# There should be stop.
return render(request, 'authors/create_author.html', context)
We here will thus create an Author object, with the given name and surname, given such author does not yet exists. If it already exists, we retrieve it, but that is not that much of a problem.
Note that in case the POST is successful, you better use a redirect here, this is the Post/Redirect/Get [wiki] architectural pattern.
You also better use the cleaned_data of your form_author, instead of using the request.POST data directly, since that is cleaned. Finally in case AuthorForm is a ModelForm [Django-doc], you better let .save() [Django-doc] do the required work.
I'm testing a form that I wrote up earlier. For some reason, the test won't pass. It's like the form is ignoring the data I pass it, and I don't see why. The traceback tells me that the user variable in the clean method of the form is None, though a User is definitely passed into the form. The traceback:
... in clean
if user.pk is not userwebsite.user.pk: AttributeError: 'NoneType' object has no attribute 'pk'
The Form:
class CreateAuditForm(forms.Form):
user = forms.ModelChoiceField(queryset=User.objects.all(), widget=HiddenInput)
website = forms.ModelChoiceField(queryset=UserWebsite.objects.all(), widget=HiddenInput)
emails = forms.CharField(
max_length=250,
required=False
)
def clean_user(self):
user = self.cleaned_data.get('user', None)
if not user.groups.filter(name__iexact='subscribed').exists() and not user.groups.filter(name__iexact='addon').exists():
raise forms.ValidationError(_("You must have an active subscription to request \
website audits. Please try again after subscribing to us."))
return user
def clean(self):
data = self.cleaned_data
user = data.get('user')
userwebsite = data.get('website', None)
if userwebsite.user:
if user.pk is not userwebsite.user.pk:
raise forms.ValidationError(_("Sorry, try again."))
elif userwebsite.addon:
if user.pk is not userwebsite.addon.pk:
raise forms.ValidationError(_("Sorry, try again."))
return self.cleaned_data
def save(self):
# Action
The Test:
class CreateAuditFormTestCase(TestCase):
def setUp(self):
super(CreateAuditFormTestCase, self).setUp()
self.form = CreateAuditForm
...
self.website = Website.objects.create(
title="permanence",
url="https://www.badabuyhere.com",
display="www.bababuyhere.com")
self.unsubscriber = User.objects.create(
username="adiagojesse",
first_name="adiago",
last_name="jesse",
email="bannerfare#coldmount.com",
password="tigermountainvalley"
)
self.unsubscriberwebsite = UserWebsite.objects.create(
user=self.unsubscriber,
website=self.website,
is_competitor=False
)
...
def test_user_validation(self):
data = {
"user":self.unsubscriber.pk,
"website":self.unsubscriberwebsite.pk,
"emails":"john#gmail.com, jeff#gmail.com"
}
self.assertTrue(self.unsubscriber)
self.assertTrue(self.unsubscriberwebsite)
audit = self.form(data)
self.assertEqual(audit.is_valid(), False)
This is probably a simple issue that I can't pick up on, which is what's frustrating me, lol. Help would be appreciated.
My guess is that in CreateAuditForm.clean, useris None because clean_user raised a ValidationError. The ValidationError comes from the fact that the user does not have the groups he needs.
Another issue I see is that to test equality between model instances in Django, you should not use primary keys but test using the instances directly, using == and not is. (See https://stackoverflow.com/a/13650309/1644198 for more information on is and ==)
Example:
if user != userwebsite.user:
# etc...
I have models for Application and Role. Role is linked to a FK Role_type, which is linked by FK to Applications that can use those Role_types (this is a bit of an over-simplication for the question, but I think it suffices). I need a way to create a form to make a new Application, and also to create records assigning associated roles to people (although they can be left blank.)
I have gotten as far as creating the form for the Application and having the associated Role-Types appear on the page, with dropdowns to be populated with a user. Hitting submit, though, didn't create any of the associated Role records. All of my research seems to keep coming back to Inline Model Forms, but the docs aren't really making sense to me--the inputs in the example don't seem to correlate to what I need.
I know this may seem like a duplicate, but trust me when I say I've looked at every SO question that seems to relate to this!
EDIT: My POST looks like this: QueryDict: {u'roles-MAX_NUM_FORMS': [u'1000'], u'roles-1-role_type': [u'4'], u'roles-0-user': [u'1'], u'app-owner': [u'1'], u'app-name': [u'1234'], u'app-serviceTier': [u''], u'app-jiraProject': [u''], u'roles-TOTAL_FORMS': [u'2'], u'roles-1-user': [u''], u'roles-0-role_type': [u'3'], u'csrfmiddlewaretoken': [u'eGsDwtsSQJfl0'], u'roles-INITIAL_FORMS': [u'2']}>. Printing RolesFormSet gives me the exact same output (see comment below)
models.py
class Item(models.model):
name = models.CharField(max_length=255)
roles = models.ManyToManyField(User, through='Role')
class Application(Item):
other_assorted_attributes = foo
class RoleType(models.Model):
name = models.CharField(max_length=255)
class ItemTypeRoleMapping(models.Model):
''' pairs role-types (e.g., Developer) with an Item class they are relevant to'''
roleType = models.ForeignKey(RoleType)
itemType = models.CharField(max_length=255, choices=itemChoices)
class Role(models.Model):
role_type = models.ForeignKey(RoleType)
user = models.ForeignKey(User)
item = models.ForeignKey(Item)
views.py
def buildRolesFormset(itemClass):
''' given an item, build a form for all associated roles '''
roleTypesForItem = ItemTypeRoleMapping.objects.all().filter(itemType=itemClass.__name__)
applicable_roles = [{'role_type': roleType} for roleType in roleTypesForItem]
# formset = rolesFormSet(initial=initial, prefix='roles')
RoleFormSet = inlineformset_factory(Application, Role, extra=len(roleTypesForItem), can_delete=False)
formset = RoleFormSet()
for subform, data in zip(formset.forms, applicable_roles):
subform.initial = data
return formset
def new(request):
''' Create a new application '''
user = request.user
# check permission
if request.method == 'POST':
appform = AppForm(request.POST, prefix='app')
if appform.is_valid():
app = appform.save(commit=False)
rolesInlineFormSet = inlineformset_factory(Application, Role)
# pdb.set_trace()
rolesFormSet = rolesInlineFormSet(request.POST, instance=app, prefix='roles')
if rolesFormSet.is_valid():
rolesFormSet.save()
else:
print rolesFormSet.errors
app = appform.save()
# check rolesFormSet
return redirect(reverse('index'))
else:
appform = AppForm(prefix='app')
rolesFormSet = buildRolesFormset(Application)
return render(request, 'who/editapp.html',
{'appform': appform,
'rolesFormSet': rolesFormSet
})
Tricky to tell without more information, but it looks like you're not saving your rolesFormset in the view. You need to call rolesFormset.save() alongside your form.save() call. Additionally, I suppose you want to attach the roles to the created app? Something like this in your view should work:
if request.method == 'POST':
form = AppForm(request.POST)
rolesFormset = RoleForm(request.POST)
if form.is_valid() and rolesFormset.is_valid():
app = form.save()
roles = rolesFormset.save()
for role in roles:
app.roles.add(role)
return redirect(reverse('index'))
Update: Presuming the models.py is out-of-date, and Role does in fact have a foreignKey to User, the problem will be that you're setting a prefix here:
rolesFormSet = rolesInlineFormSet(request.POST, instance=app, prefix='roles')
but not in your buildRolesFormset function. In that function, do:
formset = RoleFormSet(prefix='roles')