Trying out a very simple test app with viewflow.io using function based views rather than the built-in class based views. The intended idea is that a product is added and then approved (via two different views/ forms). There are two issues I cannot seem to work out:
I want to pass the Product to the approval view (so that the user doing the approval can see the summary of what they are meant to approve. I am not sure how to do this - I tried passing the product_pk via the flow.View in flows.py but this results in an error and if I leave it out then the approval view updates all records rather than the current product.
The flow.If gate in flows.py always seems to be True regardless of whether the approved field in Product has been check or not. Ideally I am hoping that the approval is recorded in the Product model rather than the process model
Probably super basic mistake/ concept I am missing - any help would be appreciated.
In models.py
class Product(models.Model):
name = models.CharField(max_length=30)
quantity = models.IntegerField()
approved = models.BooleanField(default=False)
def __str__(self):
return self.name
class ProductProcess(Process):
product = models.ForeignKey(Product, blank=True, null=True)
def approved(self):
return self.product.approved
class ProductTask(Task):
class Meta:
proxy = True
In flows.py
class ProductFlow(Flow):
process_cls = ProductProcess
task_cls = ProductTask
start = flow.Start(start_process).Next(this.approve)
approve = flow.View(approve_product).Next(this.checkapproval)
checkapproval = flow.If(cond=lambda p: p.approved()) \
.OnFalse(this.approve) \
.OnTrue(this.end)
end = flow.End()
In views.py
#flow_start_view()
def start_process(request, activation):
activation.prepare(request.POST or None,)
form = ProductForm(request.POST or None)
if form.is_valid():
Product.objects.create(
name = form.cleaned_data['name'],
quantity = form.cleaned_data['quantity']
)
activation.done()
return redirect('/test')
return render(request, 'viewflowtest/product.html', {'activation': activation, 'form': form})
#flow_view()
def approve_product(request, activation):
activation.prepare(request.POST or None,)
form = ApproveProductForm(request.POST or None)
if form.is_valid():
Product.objects.update(
approved = form.cleaned_data['approved']
)
activation.done()
return redirect('/test')
return render(request, 'viewflowtest/product.html', {'activation': activation, 'form': form})
The form that is called is a very basic ModelForm class and the URLs are exactly as is described in the demo applications on the project GitHub pages. The template has the {{ activation.management_form }} tag.
First of all, you need to link the product and process. So in start view, you can do
if form.is_valid():
product = Product.objects.create(
name = form.cleaned_data['name'],
quantity = form.cleaned_data['quantity']
)
activation.process.product = product
activation.done()
or even better, if the ProductForm is the ModelForm
if form.is_valid():
product = form.save()
activation.process.product = product
activation.done() # here is new process instance created and saved to db
So the approval view could be rewritten as::
#flow_view()
def approve_product(request, activation):
activation.prepare(request.POST or None,)
form = ApproveProductForm(request.POST or None, instance=activation.process.product)
if form.is_valid():
form.save() # here is the approved field is updated
activation.done()
return redirect('/test')
return render(request, 'viewflowtest/product.html', {'activation': activation, 'form': form})
In addition, you can take a look to the viewflow example with the function-based views - https://github.com/viewflow/cookbook/blob/master/viewflow_customization/customization/parcel/views.py
Related
I am trying to associate the user with the post. I have two models students is for user and sublists is for user posts with a foreign key(author). I am using MySQL database and using forms to store data into them. when my form.author execute in my HTML file it gives me a list of ids for all users in the databse but I am already logged in and i want to post as the logged in user without choosing. If remove it says my form is not valid which make sense since im not inputing for form.author.Since I'm using MySQL, I'm not using the built-in User authentication method, but instead comparing both email and password with the login form input. Spend too much time on this but hard to get around with this one. Any help would be appreciated
my views.py look like this
def addnew(request):
if request.method == 'POST':
form = Sublist(request.POST)
if form.is_valid():
try:
form.save()
messages.success(request, ' Subscirption Saved')
name = sublist.objects.get(name=name)
return render (request, 'subscrap/main.html', {'sublist': name})
except:
pass
else:
messages.success(request, 'Error')
pass
else:
form = Sublist()
return render(request, 'subscrap/addnew.html', {'form': form})
#login_required(login_url='login')
#cache_control(no_cache=True, must_revalidate=True, no_store=True)
def main(request):
return render(request, 'subscrap/main.html')
def mod(request):
student = students.objects.all()
return render(request, 'subscrap/mod.html' , {'students': student})
My Models.py
class students(models.Model):
fname = models.CharField(max_length=50)
lname = models.CharField(max_length=50)
password = models.CharField(max_length = 50 , null = True)
passwordrepeat = models.CharField(max_length = 50, null = True)
email = models.EmailField(max_length=150)
class Meta:
db_table = "students"
class sublist(models.Model):
author = models.ForeignKey(students, related_name='sublist' ,on_delete=models.CASCADE)
name = models.CharField(max_length=150)
cost = models.IntegerField(default = 0)
renewalcycle = models.IntegerField(default = 0)
class Meta:
db_table = "sublist"
Since I'm using forms here's my forms.py
lass StudentForm(forms.ModelForm):
class Meta:
model = students
fields = "__all__"
class Studentlogin(forms.Form):
email = forms.EmailField(max_length=150)
password = forms.CharField(max_length = 50, widget=forms.PasswordInput)
class Sublist(forms.ModelForm):
class Meta:
model = sublist
fields = "__all__"
Exclude the Author from the Sublist form:
class Sublist(forms.ModelForm):
class Meta:
model = sublist
exclude = ['author']
In the addnew method, you associate the .instance.author with the request.user:
#login_required(login_url='login')
def addnew(request):
if request.method == 'POST':
form = Sublist(request.POST)
if form.is_valid():
form.instance.author = request.user
form.save()
messages.success(request, ' Subscirption Saved')
return redirect('some_view')
else:
messages.error(request, 'Error')
else:
form = Sublist()
return render(request, 'subscrap/addnew.html', {'form': form})
Note: Models in Django are written in PascalCase, not snake_case,
so you might want to rename the model from sublist to Sublist.
Note: Usually a Form or a ModelForm ends with a …Form suffix,
to avoid collisions with the name of the model, and to make it clear that we are
working with a form. Therefore it might be better to use SublistForm instead of
Sublist.
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the students directly. For more information you can see the referencing the User model section of the documentation.
I have the following model in Django which I use to store data about medicines.
class Medicine(models.Model):
Medicine_Name = models.CharField(max_length=100)
User_Associated = models.ForeignKey(User, on_delete=models.CASCADE)
Tablets_In_Box = models.IntegerField()
Dose_in_mg = models.IntegerField()
Dose_Tablets = models.IntegerField()
Number_Of_Boxes = models.IntegerField()
Last_Collected = models.DateField()
def __str__(self):
return self.Medicine_Name
def get_absolute_url(self):
return reverse('tracker-home')
I am trying to create a model form where a user can update the last collection of one of their medicines. Here is what I began with.
class CollectionForm(forms.ModelForm):
class Meta:
model = Medicine
fields = ['Medicine_Name', 'Number_Of_Boxes', 'Last_Collected']
I do not understand how I can call an instance of my model based on the 'Medicine_Name' from the field. In other words, I need the user to be able to select the correct medicine from a dropdown menu, and then the form must update the 'Last_Collected', and 'Numer_Of_Boxes' fields on my Medicine model.
https://docs.djangoproject.com/en/2.1/topics/forms/modelforms/#the-save-method
It seems this contains relevant information, but I struggle to see how to use it in this instance. How can I correctly get the instance of the medicine form I need, based on the user input in the form? Furthermore how can I use the save method in my views to make sure the database gets updated correctly?
EDIT Added view for the form:
def update(request, pk):
instance = Medicine.objects.get(id=pk)
if request.method == 'POST':
form = CollectionForm(user=request.user, instance=instance, data=request.POST)
if form.is_valid():
instance = form.save(commit=False)
instance.User_Associated = request.user
instance.save()
else:
form = CollectionForm()
context = {'form': form}
return render(request, 'tracker/medicine_collection.html', context )
**EDIT
views:
def update(request, pk):
instance = Medicine.objects.get(id=pk)
if request.method == 'POST':
form = CollectionForm(instance=instance, data=request.POST)
if form.is_valid():
instance = form.save(commit=False)
instance.User_Associated = request.user
instance.save()
return redirect ('/')
....
This is based on updating the instance of the specific user. This tutorial helpt me achieve the same thing.
https://youtu.be/EX6Tt-ZW0so
Tried a different approach (class based views - UpdateView) I just learned here on SO. Did not test it but I think its a step in the right direction.
class UpdateMedicine(LoginRequiredMixin, UpdateView):
model = Medicine #call the model you need to update
fields = ['Medicine_Name', 'Number_Of_Boxes', 'Last_Collected'] #specify the fields you need to update
template_name_suffix = 'medicine_update_form' #specify the template where the update form is living
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context.update(
user=self.request.user, #get the current logged in user
instance=get_object_or_404(Medicine, pk=self.kwargs['pk']) #get the pk of the instance
)
return context
def form_valid(self, form):
form.instance.medicine = get_object_or_404(Medicine, slug=self.kwargs['pk'])
return super().form_valid(form) #saves the updates to the instance
def get_success_url(self):
return reverse('medicine-collection') #name of the url where your 'tracker/medicine_collection.html is living
Link the appropriate templates and urls to the above example and try some things yourself.
Link to the django docs:
https://docs.djangoproject.com/en/3.0/ref/class-based-views/generic-editing/
Good luck!
Sry for stupid question, but I don't understand. I'm trying to use Django Forms, I have 2 models
class Post(models.Model):
unit = models.ForeignKey('Unit',on_delete=models.CASCADE, primary_key=False)
and
class Unit(models.Model):
name = models.CharField(max_length=120, unique = True)
I've created a form
from django import forms
from .models import Post, Unit, StatusOfPost
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = [
'unit',
]
than I've written a view.py
def ideaNewForm(request):
unit = Unit.objects.get(name=request.POST['unit'])
user = request.user
if request.method == 'POST':
form = PostForm(request.POST)
if form.is_valid():
idea = form.save(commit=False)
idea.unit = unit
idea = Post.objects.create(
author = user,
)
return redirect('postsList')
else:
form = PostForm()
return render(request, 'post_new.html', {'form':form})
Unit matching query does not exist.- and i have that such issue.
I have a dropdown list it is a Unit model. How save it right?
Before I did it without Django Form
unit = Unit.objects.get(name=request.POST['unit'])
and it worked well, but I want use Django Forms
I'm not quite sure why you are trying to get the unit separately. It's what is selected in the form, there is no need to get it; just saving the form will create the post with the selected unit. The only thing you need to do is to add the user.
def ideaNewForm(request):
if request.method == 'POST':
form = PostForm(request.POST)
if form.is_valid():
idea = form.save(commit=False)
idea.author = request.user
idea.save()
return redirect('postsList')
else:
form = PostForm()
return render(request, 'post_new.html', {'form':form})
You just missing a ['unit'] after request.POST:
def ideaNewForm(request):
unit = Unit.objects.get(name=request.POST['unit'])
(...remaining codes...)
How can I enable the user to generate only one instance of an object “bet” with a POST method and modify it through a PUT method (for example)
forms.py
class BetForm(forms.ModelForm):
team1_score = forms.IntegerField(min_value=0, max_value=15)
team2_score = forms.IntegerField(min_value=0, max_value=15)
match = forms.ModelChoiceField(queryset=Match.objects.only('id'))
class Meta:
model = Bet
fields = ('team1_score', 'team2_score', 'match')
models.py
class Bet(models.Model):
match = models.ForeignKey(Match, on_delete=models.CASCADE, related_name='+')
team1_score = models.PositiveIntegerField(default=0)
team2_score = models.PositiveIntegerField(default=0)
def __str__(self):
return (str(self.match))
views.py
def post(self, request):
form = BetForm(request.POST)
if form.is_valid():
form.save()
team1_score = form.cleaned_data.get('team1_score')
team2_score = form.cleaned_data.get('team2_score')
match = form.cleaned_data.get('match')
form = BetForm()
return redirect ('home')
args = {'form': form, 'team1_score': team1_score, 'team2_score': team2_score, 'match': match}
return render(request, self.template_name, args)
Enable the user to generate only one instance of an object “bet”...
For that, you want to add a user field to your Bet model. Here you will save a reference to the user making the request.
class Bet(models.Model):
user = models.ForeignKey(AUTH_USER_MODEL, related_name='bets', blank=True)
match = models.ForeignKey(Match, related_name='bets')
team1_score = models.PositiveIntegerField(default=0)
team2_score = models.PositiveIntegerField(default=0)
class Meta:
unique_together = ('user', 'match')
def __str__(self):
return (str(self.match))
Notice the unique_together option which makes sure a user can only create a single Bet instance for a given match.
modify it through a PUT method (for example)
Django does not automatically parse the body for PUT requests like it does for POST. Browsers normally issue POST request on forms submission. If you still want to solve it using PUT, check this post (pun intended).
Parsing Unsupported Requests (PUT, DELETE, etc.) in Django
My suggestion is to modify your post view so it accepts an optional parameter bet_id. This you can define in urlpatterns. The view would then look like this one. You retrieve the bet if bet_id is provided and pass it to the form. This way it understands the user is modifying it.
def post(self, request, bet_id=None):
if bet_id:
bet = Bet.objects.get(pk=bet_id)
form = BetForm(request.POST, instance=bet)
else:
form = BetForm(request.POST)
if form.is_valid():
bet = form.save(commit=False)
bet.user = request.user
bet.save()
# Do what you want here
Notice that we are not saving the form immediately (commit=False), so we could assign it to a user later on. This user is the logged in user from the request object.
i created a model which contains name and price. I made a view that will help me with adding products on my site:
def new_expense(request):
if request.method == "EXPENSE":
form = ExpenseForm(request.EXPENSE)
if form.is_valid():
expense = form.save(commit=False)
expense.save()
return redirect('homebudget.views.expense_detail', pk=expense.pk)
else:
form = ExpenseForm()
return render(request, 'homebudget/edit_expense.html', {'form': form})
Now i have something like that: http://i.stack.imgur.com/3RbQr.png
but when i click save there's nothing happening! What must i change in view?
forms.py:
class ExpenseForm(forms.ModelForm):
class Meta:
model = Expense
fields = ('name', 'price',)
models.py:
class Expense(models.Model):
name = models.CharField(max_length=40)
price = models.FloatField("price")
request.method cannot be equal to EXPENSE, it may only be a HTTP method name. In the same way, request.EXPENSE
is not defined.
I don't know how the request is done but what you probably want to test is:
if request.method == 'POST':
form = ExpenseForm(request.POST)
Note:
expense = form.save(commit=False)
expense.save()
and
expense = form.save()
are equivalent.