Ecto validate_required of either of two attributes - ecto

What's the best way to do a validation of the presence of either of two attributes for a changeset?
The validation should only fail if neither of the two attributes is provided
e.g. consider a User model with attributes :name, :uid, :email, :phone
:name always needs to be present
either :email or :uid should be present
:phone is optional
Would this work?
def changeset(struct, params \\ %{}) do
struct
|> cast(params, [:name,:email,:uid,:phone])
|> validate_required([:name, :email]) || validate_required([:name, :uid]
end
end

You may start with something like:
def changeset(struct, params \\ %{}) do
struct
|> cast(params, ~w(name email uid phone)a)
|> validate_required(:name)
|> validate_required_params(params)
end
end
def validate_required_params(changeset, %{"uid" => _}) do
changeset
|> validate_required(:uid)
end
def validate_required_params(changeset, %{"email" => _}) do
changeset
|> validate_required(:email)
end
def validate_required_params(changeset, _params) do
changeset
|> add_error(:params, "either :uid or :email is required")
end
But you need to also check for example if both uid and email are provided. What in such case?

Related

i want to create a search in django which have order by istartswith first than icontains

def autocomplete(request):
template_name='searchresults.html'
if 'term' in request.GET:
qs=Post.objects.filter(Q(title__istartswith=request.GET.get('term'))|Q(content__icontains=request.GET.get('term')))
titles=list()
for post in qs:
titles.append(post.title)
return JsonResponse(titles, safe=False)
return render(request, template_name)
How can I can order them ins such a way that if it begins with term order it as first and the one that contains it but does not begin with it as second
You can make use of .union(…) [Django-doc]:
term = request.GET.get('term')
qs = Post.objects.filter(
title__istartswith=term
).union(Post.objects.filter(
content__icontains=term
))
or with an .annotate(…) [Django-doc]:
from django.db.models import BooleanField, ExpressionWrapper, Q
term = request.GET.get('term')
qs = Post.objects.filter(
Q(title__istartswith=term) | Q(content__icontains=term)
).annotate(
is_start=ExpressionWrapper(
Q(title__istartswith=term),
output_field=BooleanField()
)
).order_by('-is_start')

how to fix "Rendered ActiveModel::Serializer::Null with Hash"

I am trying to write API for user model, where i have to return only two columns with some modification(appending string)
Every thing work's fine, I even get the correct result, but when I see status code its showing '500', when i check the logs its showed the following error
[active_model_serializers] Rendered ActiveModel::Serializer::Null with Hash
following is the code
1. users_controller.rb
class Api::V1::UsersController < Api::V1::ApiController
# GET
def pl_details
render json: {pl: current_user.pl_url, enabled: current_user.personal_calendar_enabled}, status: :success
end
...
end
user.rb
...
def pl_url
return "#{Rails.application.secrets.app_host}/#{self.calendar_url_token}"
end
...
user_serializer.rb
class UserSerializer < ActiveModel::Serializer
attributes :id, :firstname, :lastname, :email
end
Never mind,
I just did it other way around,I used a separate Serializer to avoid the error, following is the approach
class Api::V1::UsersController < Api::V1::ApiController
# GET
def pl_details
render json: current_user,serializer: PLSerializer, status: :success
end
...
end
and inside PLSerializer
class PLSerializer < ActiveModel::Serializer
attributes :pl, :personal_calendar_enabled
def personal_link
current_user.pl_url
end
end

How to generate multiple nested changesets with cast_assoc

I have Memberships, Lists, Items and Memories defined as follows :
schema "lists" do
has_many :itemlists, Learnit.Itemlist, on_delete: :delete_all
many_to_many :items, Learnit.Item, join_through: Learnit.Itemlist
has_many :memberships, Learnit.Membership, on_delete: :delete_all
end
schema "memberships" do
belongs_to :list, Learnit.List
has_many :memorys, Learnit.Memory, on_delete: :delete_all
end
schema "itemlists" do
belongs_to :item, Learnit.Item
belongs_to :list, Learnit.List
end
schema "items" do
has_many :itemlists, Learnit.Itemlist
many_to_many :lists, Learnit.List, join_through: Learnit.Itemlist
has_many :memorys, Learnit.Memory
end
schema "memorys" do
belongs_to :membership, Learnit.Membership
belongs_to :item, Learnit.Item
end
My Membership's model
def changeset(struct, params \\ %{}) do
struct
|> cast(params, [:user_id, :list_id])
|> validate_required([:user_id, :list_id])
|> cast_assoc(:memorys)
end
My Memory's model
def changeset(struct, params \\ %{}) do
struct
|> cast(params, [:status, :membership_id, :item_id])
|> foreign_key_constraint([:membership_id, :item_id])
|> unique_constraint(:membership_id_item_id)
|> validate_required([:membership_id, :item_id])
end
I try to create a new membership and generate his memories associated with one query. I need to get first Items that are associated with the Membership through its list. Then my plan is to use Membership's cast_assoc property to save the whole in Repo.
How can I load these items in the Membership's changeset?
Items are already loaded correctly, I get a Map.put/4 Undefined...
def create(conn, %{"membership" => membership_params}) do
memories = %{}
list =
List
|> Repo.get!(membership_params["list_id"])
|> Repo.preload(:items)
|> Map.get(:items) # Get the list of items
|> Enum.map(&load_items(&1, memories)) # Loop through the list to get each item
IO.inspect(memories)
Map.put(membership_params, :memorys, memories)
membership_with_memories = Membership.changeset(%Membership{}, membership_params)
case Repo.insert(membership_with_memories) do
{:ok, _} ->
conn
|> put_flash(:info, "Membership created successfully.")
|> redirect(to: list_path(conn, :index))
{:error, membership_with_memories} ->
Logger.debug("Membership : failed to save membership")
conn
|> put_flash(:alert, "Membership was not created.")
|> redirect(to: topic_path(conn, :index))
end
end
defp load_items(item, memories) do
item
|> Map.put(memories, :item, item) # Add item to the hash
end
I found my way to load the params correctly, but still I cant find the way to save it in repo (I get a "cannot convert the given list to a string").
%{"list_id" => "1",
"memorys" => [%{"item_id" => "1"}, %{"item_id" => "2"}, %{"item_id" => "3"}],
"user_id" => "1"}
def create(conn, %{"membership" => params}) do
memories = []
params =
List
|> Repo.get!(params["list_id"])
|> Repo.preload(:items)
|> Map.get(:items) # Get the list of items
|> Enum.map(&add_items(&1, memories)) # Loop through the list to get list of item ids
|> (&Map.put(params, "memorys", &1)).() # Add the list to membership params
|> IO.inspect()
membership_with_memories = Membership.changeset(%Membership{}, params)
case Repo.insert(membership_with_memories) do
{:ok, _} ->
conn
|> put_flash(:info, "Membership created successfully.")
|> redirect(to: list_path(conn, :index))
{:error, membership_with_memories} ->
Logger.debug("Membership : failed to save membership")
conn
|> put_flash(:error, "Membership was not created.")
|> redirect(to: list_path(conn, :index))
end
end
defp add_items(item, memories) do
memories ++ %{"item_id" => Kernel.inspect(item.id)} # Make sure id is a string
end
Just found the problem in my Memory's model. I don't really get why the validate_required blocks the cast_assoc, but it is fine for me...
def changeset(struct, params \\ %{}) do
struct
|> cast(params, [:status, :membership_id, :item_id])
#|> foreign_key_constraint([:membership_id, :item_id]) # Dont use that !
|> unique_constraint(:membership_id_item_id) # Dont forget to put constraint on table too
|> validate_required([:item_id]) # Cannot use :membership_id with cast_assoc
end

AwesomeWM client created/removed callback

I am using awesome WM and I want to run a lua function after a client is created/deleted. Specifically, I want to change the name of a tag to the name of one of the clients that are on the tag.
I do this with a timer, but I think the best way to do this would be to register a callback function to awesomeWM that it will invoke when a client is created/removed.
Are there some hooks/callbacks that I can implement to tell awesome to do this for me?
---------------------------------------------UPDATE----------------------------------------
I tried using the signals, but i cant find the correct signal that changes calls my function AFTER the window is created and attached to the tag. I tried this with manage/unmanage tagged/untagged, and tag.new, etc, but no one helps.
Any ideas?
here is the code:
override_name_char = "<"
function tag_name_from_client(c)
if string.match(c.name, "Mozilla Firefox") then
return "Firefox"
end
if string.match(c.name, "Sublime Text") then
return "Sublime"
end
if string.match(c.name, "/bin/bash") then
return "Shell"
end
return ""
end
function tag_name_from_tag(tag)
if tag.name:match(override_name_char) then
return tag.name
end
for _, c in pairs(tag:clients()) do
return " "..tostring(awful.tag.getidx(tag)).." "..tag_name_from_client(c)
end
return tostring(awful.tag.getidx(tag))
end
function refresh_tag_name()
for s = 1, screen.count() do
for _,tag in pairs(awful.tag.gettags(s)) do
tag.name = tag_name_from_tag(tag)
end
end
end
client.connect_signal("tagged", refresh_tag_name)
client.connect_signal("untagged", refresh_tag_name)
--tag_timer = timer({timeout = 0.5})
--tag_timer:connect_signal("timeout", function()
--refresh_tag_name()
--end)
--tag_timer:start()
Thanks in advance for any help regarding this.
One of possible ways for v3.5.6, try this in your rc.lua
local naughty = require("naughty")
client.connect_signal("manage", function (c)
--filter client by class name
if c.class:lower() == "gedit" then
-- do things on client start
naughty.notify({text = "Gedit launched!"})
-- add exit signal for this client
c:connect_signal("unmanage", function() naughty.notify({text = "Gedit closed!"}) end)
end
end)
"A new client is created" is the manage signal.
"A new client was destroyed" is the unmanage signal.
So, something like the following (untested):
local function choose_name_for_tag(t)
for _, c in ipairs(t:clients() do
return "has client: " .. tostring(c.name or "unknown")
end
return "has no clients"
end
local function update_state()
for _, t in pairs(root.tags()) do
t.name = choose_name_for_tag(t)
end
end
client.connect_signal("manage", update_state)
client.connect_signal("unmanage", update_state)
tag.connect_signal("tagged", function(t)
t.name = choose_name_for_tag(t)
end)
tag.connect_signal("untagged", function(t)
t.name = choose_name_for_tag(t)
end)

XTEND For-Loop indexcontrol in DomainmodelGenerator.xtend (XTEXT codegeneration project)

I worked through the Tutorials at eclipse.org/Xtext/documentation and get into expanding these samples. Working with the Domainmodel.xtext sample I generate a Java-Classfile for each entity as stated in the Tut.
The DSL specifies an arbitry number of features, aka class properties:
Entity:
'entity' name = ID
('extends' superType = [Entity | QualifiedName])?
'{'
(features += Feature)*
'}'
;
In DomainmodelGenerator.xtend then I added code to generate a JAVA-classconstructor. The XTEND-Forloop cycles through all arguements - looks like this:
def compile_Constructors(Entity e) '''
public «e.name.toFirstUpper»
(
«FOR f : e.features»
«f.type.fullyQualifiedName» «f.name.toFirstUpper»,
«ENDFOR»
)
{}
'''
Problem
With this the last parameter there is still a comma emitted. How can I get control in XTEND over the loopindex, to make the generator to emit legal JAVA code?
The «FOR» loop has some options which are quite handy:
BEFORE string
SEPARATOR string
AFTER string
These allows you to emit additional strings before, between and after items. If there are no items (empty list) none of them is emitted.
So in your case just use
«FOR f : e.features SEPARATOR ', '»
How about:
def compile_Constructors(Entity e) '''
public «e.name.toFirstUpper»
(
«e.features.map[type.fullyQualifiedName + ' ' + name.toFirstUpper].join(', ')»
)
{}
'''

Resources