I'm new to Django/Python. I have a problem with djangos MultipleChoiceField in that it does not display both in the loaded HTML code in the browser and on the displayed page. Other aspects of the form have been successfully rendered.
models.py
SERVICE_A = "1"
SERVICE_B = "2"
SERVICE_CHOICE =(
(SERVICE_A, "Internet"),
(SERVICE_B, "Television"),
)
class Contract(models.Model):
priority_service = ChoiceArrayField(models.CharField(max_length=15, choices=SERVICE_CHOICE), default=list)
forms.py
class ContractCreateForm(forms.ModelForm):
priority_service = forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple, choices=SERVICE_CHOICE)
class Meta:
model = Contract
widgets = {
"name": rows(TextFormControl(), 1),
"city": TextFormControl(),
"address": TextFormControl(),
"phone": TextFormControl(),
"from_office": CheckBoxFormControl(),
}
fields = widgets.keys()
views.py
def contract_new(request):
form = ContractCreateForm()
if request.method == "POST":
form = ContractCreateForm(request.POST)
if form.is_valid():
contract = form.save(commit=False)
priority_service = form.cleaned_data.get('priority_service')
contract.created_by = request.user
contract.status = STATUS_NONE
contract.save()
messages.add_message(request, messages.SUCCESS, f"Успешно создано под номером #{contract.id}")
form = ContractCreateForm() # reset form
return render(request, "register_contract.html", {"form": form})
The problem is that I can't display the option with the "name", contract.get_priority_service_display - does not transmit any result. Although if I just output contract.priority_service, it will pass the string: "1" "2".
There were also attempts to check at the stage of selecting information from the form via get in views.py :
print(priority_service) - it also outputs ['1', '2']. But for some reason I can't pick up the name (inet, tv).
Although many other fields (Standard) are displayed normally.
Related
I am using fastapi and BaseModel from pydantic to validate and document the JSON schema for an API return.
This works well for a fixed return but I have optional parameters that change the return so I would like to include it in the validation but for it not to fail when the parameter is missing and the field is not returned in the API.
For example: I have an optional boolean parameter called transparency when this is set to true I return a block called search_transparency with the elastic query returned.
{
"info": {
"totalrecords": 52
},
"records": [],
"search_transparency": {"full_query": "blah blah"}
}
If the parameter transparency=true is not set I want the return to be:
{
"info": {
"totalrecords": 52
},
"records": []
}
However, when I set that element to be Optional in pydantic, I get this returned instead:
{
"info": {
"totalrecords": 52
},
"records": [],
"search_transparency": None
}
I have something similar for the fields under records. The default is a minimal return of fields but if you set the parameter full=true then you get many more fields returned. I would like to handle this in a similar way with the fields just being absent rather than shown with a value of None.
This is how I am handling it with pydantic:
class Info(BaseModel):
totalrecords: int
class Transparency(BaseModel):
full_query: str
class V1Place(BaseModel):
name: str
class V1PlaceAPI(BaseModel):
info: Info
records: List[V1Place] = []
search_transparency: Optional[Transparency]
and this is how I am enforcing the validation with fastapi:
#app.get("/api/v1/place/search", response_model=V1PlaceAPI, tags=["v1_api"])
I have a suspicion that maybe what I am trying to achieve is poor API practice, maybe I am not supposed to have variable returns.
Should I instead be creating multiple separate endpoints to handle this?
eg. api/v1/place/search?q=test vs api/v1/place/full/transparent/search?q=test
EDIT
More detail of my API function:
#app.get("/api/v1/place/search", response_model=V1PlaceAPI, tags=["v1_api"])
def v1_place_search(q: str = Query(None, min_length=3, max_length=500, title="search through all place fields"),
transparency: Optional[bool] = None,
offset: Optional[int] = Query(0),
limit: Optional[int] = Query(15)):
search_limit = offset + limit
results, transparency_query = ESQuery(client=es_client,
index='places',
transparency=transparency,
track_hits=True,
offset=offset,
limit=search_limit)
return v1_place_parse(results.to_dict(),
show_transparency=transparency_query)
where ESQuery just returns an elasticsearch response.
And this is my parse function:
def v1_place_parse(resp, show_transparency=None):
"""This takes a response from elasticsearch and parses it for our legacy V1 elasticapi
Args:
resp (dict): This is the response from Search.execute after passing to_dict()
Returns:
dict: A dictionary that is passed to API
"""
new_resp = {}
total_records = resp['hits']['total']['value']
query_records = len(resp.get('hits', {}).get('hits', []))
new_resp['info'] = {'totalrecords': total_records,
'totalrecords_relation': resp['hits']['total']['relation'],
'totalrecordsperquery': query_records,
}
if show_transparency is not None:
search_string = show_transparency.get('query', '')
new_resp['search_transparency'] = {'full_query': str(search_string),
'components': {}}
new_resp['records'] = []
for hit in resp.get('hits', {}).get('hits', []):
new_record = hit['_source']
new_resp['records'].append(new_record)
return new_resp
Probably excluding that field if it is None can get the job done.
Just add a response_model_exclude_none = True as a path parameter
#app.get(
"/api/v1/place/search",
response_model=V1PlaceAPI,
tags=["v1_api"],
response_model_exclude_none=True,
)
You can customize your Response model even more, here is a well explained answer of mine I really suggest you check it out.
I have a requirement of adding Value to Custom attribute in G Suite for bulk users, I have added address and other field using App script but don't know how to add values to a custom attribute named "Enhanced Desktop Security" as shown in the image below.
Value to be added is using App Script is: "un:Windows"
Request your help with the Script.
I have been working on this and came to know that 1st you have to identify the schemas of the custom attribute here : https://developers.google.com/admin-sdk/directory/v1/reference/users/list Once you do that you can use the below script. Please make sure that you change the schema in the below script.
Mention "User Email ID", "Value", "Updation Status" in Cloumn A,B,C, respectively in sheet.
function updateCustomE() {
var ss = SpreadsheetApp.openById(""); // Mention ID of the spreadsheet here.
var sheet = ss.getSheetByName(""); // Mention Name of the sheet here.
var values = sheet.getDataRange().getValues();
var fileArray = [["Updation Status"]]
for(i=1; i <values.length; i++)
{
var userKey = values[i][0]
var customValue = values [i][1]
try{
var status = "Value not updated"
var status = AdminDirectory.Users.update({
"customSchemas": {
"Enhanced_desktop_security" : {
"Local_Windows_accounts" : [
{
"type": "custom",
"value": customValue
}
]
}
}
}, userKey);
if (status != "Value not updated"){
status = "Value updated Successfully"
}
}
catch (e) {
Logger.log(e.message)
var status = e.message
}
fileArray.push([status])
}
var range = sheet.getRange(1, 3, fileArray.length, 1).setValues(fileArray)
}
I have a form as following:
from flask_wtf import Form
class MyForm(Form):
domain = StringField('domain')
hosts = StringField('hosts')
startdate = StringField('Start Date', validators=[InputRequired()])
enddate = StringField('End Date', validators=[InputRequired()] )
starthour = SelectField('Start Hour',coerce=int, choices=[(i, i) for i in range(0, 24)], default=0)
endhour = SelectField('End Hour',coerce=int, choices=[(i, i) for i in range(0, 24)], default=23)
submit1 = SubmitField('Submit1')
submit2 = SubmitField('Submit2')
I try to test it using unittest:
def test_form(self):
form = {
"domain": "fake",
"hosts": "fake",
"startdate": "fake",
"enddate": "fake",
"starthour": 1,
"endhour": 2,
'submit1': True,
'submit2': None
}
response = self.client.post(url_for('/'), data=form)
But though the printed received data looks correct, the function form.validate_on_submit() can never pass. Does anyone here know the write way to feed the form data for post request?
Thanks,
I found the root cause after digging into the flask_wtf source code. The validation failure is from hidden element "csrf_token". I think there are many ways to solve it but I do it by passing argument when creating MyForm object in the view function:
form = MyForm(csrf_enabled=False)
Can someone pls explain how can I filter users posts if user is subscriber or not?
'QuerySet' object has no attribute 'subs'
I use custom user
class Every(AbstractBaseUser):
user = models.OneToOneField(settings.AUTH_USER_MODEL, unique=True)
name = models.CharField(max_length=30, blank=True)
Here is post model:
class Post(models.Model):
user = models.ForeignKey(User, blank=True, null=True)
text = models.TextField(max_length=1200)
A subscriber model:
class Sub(models.Model):
user = models.OneToOneField(User, related_name='user')
subs = models.ManyToManyField(User, blank=True, related_name='subs')
A view that I'm trying to use:
def tape(request, every_id):
context = {}
context.update(csrf(request))
post_form = PostForm
pform = post_form
sub = Sub.objects.filter(subs=every_id)# here I get users that intersting for my user
tape = Post.objects.filter(user=sub.subs).order_by("-timestamp")
username = request.user
context = {"username": username, "pform": pform, "tape": tape, "sub": sub,}
return render(request, 'tape.html', context)
actually it would be better to use a class view. But here is an answer with an usual view. We need to get subscription's and subscribers's ids before we start filter posts
here is even a bit more :-)
def get_user_id_list(user):
"""Returns a list of subscribers's ids"""
try:
sub = user.user
Sub.DoesNotExist:
return []
return sub.subs.all().values_list('user_id', flat=True)
def get_user_id_list_2(user):
"""Returns a list of subscription's ids"""
return user.subs.values_list('user_id', flat=True)
def tape(request):
pform = PostForm
user_id_list = get_user_id_list_2(request.user)
logger.info('user_id_list = {}'.format(user_id_list))
tape = Post.objects.filter(user_id__in=user_id_list).order_by("-timestamp")
username = request.user
context = {
"username": username,
"pform": pform,
"tape": tape,
"sub": sub,
}
return render(request, 'tape.html', context)
There is a real lack of documentation on how to work with WTForms' FieldList. So thanks to the internet I've been able to hack together the following:
Form:
class BranchForm(Form):
name = StringField('Name', validators = [Required()])
equipment = FieldList(SelectField('Equipment', validators=[Required()], coerce=int,
choices = [(x.id, x.name) for x in Equipment.query.all()]))
mod = FieldList(StringField('Method of Delivery', validators = [Optional()]))
View:
def edit_branch(id):
branch = Branch.query.filter_by(id=id).first()
#populate data_in to be used by BranchForm
data_in = []
for eq_obj in branch.equipment_assoc:
data_in.append(('equipment', eq_obj.equipment.id))
data_in.append(('mod', eq_obj.mod))
editform = BranchForm(data=MultiDict(data_in))
if editform.validate_on_submit():
branch.name = editform.name.data
db.session.add(branch)
db.session.commit()
return redirect('/admin/branches/' + str(branch.id))
editform.name.data = branch.name
return render_template("branch_edit.html",
title="Edit Branch",
branch = branch,
editform = editform)
What's throwing me off is that everywhere else that I've used a WTForm Form and populated the fields with data from my db (like for edit forms), I've had to populate these form fields after the form.validate_on_submit() block, because if not, then the form will never update as whatever is submitted is immediately overwritten.
See "editform.name.data = branch.name" (this is how I've always done it)
From every example I've found online about populating a FieldList, it apparently must be done during instantiation, but the form has to be instantiated before the validate_on_submit() as well because validate_on_submit() is a method of the form object.
See "editform = BranchForm(data=MultiDict(data_in))" (this is how I've seen FieldLists populated in all the examples I've seen.)
How can I go about populating my form with its field lists?
Alright, so a buddy helped me figure this one out. Here's what I ended up with:
Form:
class BranchForm(Form):
name = StringField('Name', validators = [Required()])
equipment = FieldList(SelectField('Equipment', validators=[Required()], coerce=int,
choices = [(x.id, x.name) for x in Equipment.query.all()]))
mod = FieldList(StringField('Method of Delivery', validators = [Optional()]))
def populate_assoc(self, branch_obj):
i = 0
branch_obj.name = self.name.data
for assoc_obj in branch_obj.equipment_assoc:
assoc_obj.equipment_id = self.equipment[i].data
assoc_obj.mod = self.mod[i].data
i += 1
View:
def edit_branch(id):
branch = Branch.query.filter_by(id=id).first()
if request.method == 'POST':
editform = BranchForm()
if editform.validate_on_submit():
editform.populate_assoc(branch)
db.session.add(branch)
db.session.commit()
return redirect('/admin/branches/' + str(branch.id))
#populate data_in to be used
data_in = []
for eq_obj in branch.equipment_assoc:
data_in.append(('equipment', eq_obj.equipment.id))
data_in.append(('mod', eq_obj.mod))
editform = BranchForm(data=MultiDict(data_in))
editform.name.data = branch.name
return render_template("branch_edit.html",
title="Edit Branch",
branch = branch,
editform = editform)
The trick was really to step away from using form.validate_on_submit() as my logic separator, since it relies on the form object. His idea was to use the if request.method == 'POST': for this purpose. This way I can instantiate my form in two different ways. One gets populated for display, the other is only instantiated if the request method is POST, thus retaining the information submitted in the form.
To finish the job I added the populate_assoc method to my form class so that I can easily place the information from the form into my association model.
WtForms has a populate_obj() method. Maybe that's what you're after?
def edit_branch(id):
branch = Branch.query.filter_by(id=id).first()
editform = BranchForm(obj=branch)
if editform.validate_on_submit():
editform.populate_obj(branch)
db.session.commit()
return redirect('/admin/branches/' + str(branch.id))
return render_template("branch_edit.html",
title="Edit Branch",
branch = branch,
editform = editform)